Setup
rm(list = ls(all.names = TRUE))
if(!require("pacman")) install.packages("pacman"); library(pacman)
pacman::p_load(
tidyverse,
scales,
janitor,
GCLr,
coin
)
knitr::opts_chunk$set(fig.width = 10)
knitr::opts_chunk$set(echo = TRUE)
.username = readLines("~/usr_pw.txt", n = 1) # LOKI username
.password = readLines("~/usr_pw.txt" , n = 2)[[2]] # LOKI password
Objective
The purpose of this notebook is to provide Wes Larson (NOAA ABL) with
adaptive run timing genotypes (9 loci) for AHRP chum samples. Filter out
plates undergoing QC crosschecks due to poor genotyping success. Wes is
interested in representation across years, less across multiple streams.
He wants to see how the patterns hold up over time. Might as well send
everything we have though!
Steps
- import all existing genotypes for CM064-CM070
- filter out untrustworthy plates that need QC cross checks
- standard GT-seq genotype QA (missing, heterozygosity,
duplicate)
- join sample date and spawning state <“../OceanAK/AHRP Salmon
Biological Data 20240105_122404.368459_with_spawning_state.csv”>
- filter for 9 adaptive run timing loci
- export genotypes with date/spawning state
Background
An adaptive marker panel associated with run timing variation in chum
salmon was developed by NOAA ABL and used to genotype two plates of
samples from the ADFG Alaska Hatchery Research Program (AHRP) study
being conducted within SE Alaska. AHRP samples (Fish Creek - Douglas
Island 2023 + Prospect Creek 2017/2018) were genotyped in August 2024
for 20 single nucleotide polymorphisms and weighted linear regressions
were performed to assess the association of different SNPs with run
timing (collection date). The chum salmon adaptive marker panel contains
20 loci spread among 5 chromosomes and was developed from variation
detected between Yukon River summer (Gisasa River) and fall (Pelly
River) run chum salmon with low coverage whole genome sequencing. These
populations spawn approximately two months and over 1500 river km apart.
NOAA targeted the major peaks in differentiation with particular
interest in markers within the genes leucine rich repeat containing nine
(LRRC9; LG35) and estrogen receptor β (ESRB; LG29) as they were
associated with return timing in other salmonid species. Eight SNPs
(four on LG29 and four on LG35) appear to be associated with run timing
within these collections. The strength of association for AHRP samples
appears to vary by population. No markers on LGs 5, 23 or 15 appear to
have a strong association with run timing within these collections.
Overall, the results from LG29 and LG35 are promising and variation in
these SNPs should be surveyed for a larger collection of samples.
A total of 9 adaptive, run timing loci were added to the AHRP SEAK
Chum parentage panel:
- OkeV2_LG23_10546237
- OkeV2_LG29_25450752
- OkeV2_LG29_25455833
- OkeV2_LG29_25483595
- OkeV2_LG29_25515161
- OkeV2_LG35_28078867
- OkeV2_LG35_28128687
- OkeV2_LG35_28165076
- OkeV2_LG35_28167172
All AHRP Chum samples from Fish Creek - Douglas Island, Prospect
Creek, Admiralty Creek, and Sawmill Creek were genotyped for the
parentage panel at the ADF&G Gene Conservation Laboratory (GCL) in
Winter/Spring 2025. Due to poor tissue quality, there was a high
proportion of samples that failed to genotype and/or appear to be
contaminated (high heterozygosity), resulting in low QC power for
several plates. QC crosschecks are currently underway at the GCL in July
2025, however, Wes wants whatever genotypes we have on hand now so will
just filter out those plates.
Import Data
LocusControl
Create objects, create LocusControl for
Chum_AHRP_257_v1.3.0.
fs::dir_create("objects")
GCLr::create_locuscontrol(markersuite = "Chum_AHRP_257_v1.3.0",
username = .username,
password = .password)
GCLr::save_objects(objects = "LocusControl", path = "objects")
Hrmm, only 251 loci. Confirm that these are the same loci run in the
most recent AHRP Chum project CM069
probe_loci_CM069 <- readr::read_tsv(
file = "V:/Lab/Genotyping/SNP Projects/Chum/Project CM069 AHRP Parentage/Genotype Data Files/set2rr_CM069_outputs/probes.txt",
show_col_types = FALSE) %>%
dplyr::distinct(`#Locus_ID`) %>%
dplyr::pull()
all.equal(probe_loci_CM069, LocusControl$locusnames)
Yes, all are the same.
loci251 <- LocusControl$locusnames
GCLr::save_objects(objects = "loci251", path = "objects")
Genotypes
Read in genotypes by project to get sillyvec.
rm(LocusControl)
# GCLr::loki2r_proj(project_name = paste0("CM0", 64:70), username = .username, password = .password) # comment out to avoid re-running
Confirm collections.
(sillyvec <- sort(project_sillys))
Subset sillyvec to remove alevin collection.
(sillyvec <- grep(pattern = "a", x = sillyvec, ignore.case = FALSE, value = TRUE, invert = TRUE))
Wipe out .gcl objects and re-read in using
loki2r.
GCLr::save_objects(objects = "sillyvec", path = "objects")
rm(list = objects(pattern = ".gcl"))
rm(LocusControl)
Read in genotypes.
GCLr::load_objects(path = "objects", pattern = "LocusControl")
GCLr::loki2r(sillyvec = sillyvec,
username = .username,
password = .password,
test_type = "GTSNP")
Only took 5.39 minutes to read everything in at the office!
Save raw genotypes (useful for working offsite and off VPN).
fs::dir_create("data/genotypes")
fs::dir_create("data/genotypes/raw")
# GCLr::save_sillys(sillyvec = sillyvec, path = "data/genotypes/raw", rds = TRUE) # hide so you don't overwrite on accident!
Re-load
Re-load, if necessary
GCLr::load_objects(path = "objects")
[1] "loci251" "LocusControl" "sillyvec"
GCLr::load_sillys(sillyvec = sillyvec, path = "data/genotypes/raw", rds = TRUE)
[1] "CMADMCR13.gcl" "CMADMCR14.gcl" "CMADMCR17.gcl" "CMADMCR18.gcl" "CMFISHCR13.gcl" "CMFISHCR14.gcl" "CMFISHCR17.gcl" "CMFISHCR18.gcl"
[9] "CMFISHCR19.gcl" "CMFISHCR20.gcl" "CMFISHCR21.gcl" "CMFISHCR22.gcl" "CMFISHCR23.gcl" "CMFISHCRT13.gcl" "CMFISHCRT21.gcl" "CMFISHCRT22.gcl"
[17] "CMFISHCRT23.gcl" "CMPROSCR13.gcl" "CMPROSCR14.gcl" "CMPROSCR17.gcl" "CMPROSCR18.gcl" "CMPROSCR19.gcl" "CMPROSCR20.gcl" "CMPROSCR21.gcl"
[25] "CMPROSCR22.gcl" "CMPROSCRT21.gcl" "CMPROSCRT22.gcl" "CMSAWCR13.gcl" "CMSAWCR14.gcl" "CMSAWCR15.gcl" "CMSAWCR17.gcl" "CMSAWCR18.gcl"
[33] "CMSAWCR19.gcl" "CMSAWCR20.gcl" "CMSAWCR21.gcl" "CMSAWCR22.gcl" "CMSAWCRT21.gcl" "CMSAWCRT22.gcl"
Metadata
Read in paired metadata from Salmon Biological Fact and Stream
Specimens, format similar to pws_pink_ped_dat.
NOTE I’m doing this in a bit of a hurry, so make sure
to proof read carefully if recycling this code!
(metadata <- readr::read_csv(file = "../OceanAK/AHRP Salmon Biological Data 20240105_122404.368459_with_spawning_state.csv"))
Rows: 27046 Columns: 25
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (10): silly_code, spawning_state, sex, tissue_type, dna_tray_code, location_code, sample_id, otolith_mark_present, otolith_mark_id, otolith_...
dbl (9): collection_id, fish_id, length_mm, dna_tray_well_code, sample_year, is_missing_paired_data_exists, well_has_more_than_one_sample, fw_a...
lgl (5): target_dna_tray_code, target_dna_tray_well_pos, target_container_array_type_id, container_array_type, determination_collection_id
date (1): sample_date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Modify to make similar to pws_pink_ped_dat, despite lack
of location data from riverdist.
(
metadata <- metadata %>%
dplyr::select(-dplyr::contains("target")) %>%
dplyr::select(-dplyr::contains("determination")) %>%
dplyr::rename(date = sample_date,
length = length_mm,
silly = silly_code) %>%
tidyr::unite(col = "sample", dna_tray_code:dna_tray_well_code, sep = "_", remove = FALSE) %>%
tidyr::unite(col = "silly_source", c(silly, fish_id), sep = "_", remove = FALSE) %>%
dplyr::mutate(franz_id = stringr::str_c(stringr::str_sub(silly, 1, 2), # 1st letter of species name, 1st letter of stream name
stringr::str_sub(silly, -2, -1), # 2 digit sample year
"_",
stringr::str_pad(fish_id, width = 5, pad = "0", side = "left")), # 5 digit fishid using padded zeros up front)
year = lubridate::year(date),
stream = stringr::str_remove(string = location_code, pattern = " Creek"),
DOY = lubridate::yday(date), # day of year
sex = dplyr::case_when(
sex == "M" ~ "Male",
sex == "F" ~ "Female",
sex == "U" ~ NA_character_,
is.na(sex) ~ NA_character_),
origin = dplyr::case_when(
otolith_mark_present == "NO" ~ "Natural",
otolith_mark_present == "YES" ~ "Hatchery"
), # add origin variable
origin = base::factor(origin, levels = c("Natural", "Hatchery")), # make factor to ensure hatchery != red
spawning_state = dplyr::case_when(
spawning_state == "Unknown" ~ NA_character_,
TRUE ~ spawning_state
) # force Unknown to NA
) %>%
dplyr::select(
franz_id, # unique identifier for FRANz (e.g. PE16_00001, species, stream, year, GCL fish_id)
sample, # ultimate data key for AHRP, DWP barcode + DWP position
silly_source, # unique identifier for LOKI (GCL database)
stream, # 5 pedigree streams
year, # sample year (aka death year for FRANz)
origin, # hatchery or wild
# hatchery, # which hatchery
sex, # male, female, unknown
date, # sample date
DOY, # day of year (aka julian sample date)
length, # mideye to hypural plate length in mm
# intertidal, # sampling location above or below intertidal
# distance_mouth, # riverdist approximate sampling location from mouth in meters
spawning_state, # alive, pink gill, grey gill, rotting (aka index of stream life)
# pre_spawn, # TRUE/FALSE (added in 2016); if the eggs and milt do not easily release and carcass has completely full gonads
# partial_spawn, # TRUE/FALSE (added in 2015); fish is sampled (dead or alive) with more than a little eggs or milt
# preyed_upon, # TRUE/FALSE (added in 2015); gonads removed by predators
# stream_trib, # stream tributary (Gilmour, Paddy, and Stockdale have multiple)
# latitude, # sampling area (where fish were moved to prior to sampling, rough approximate of spawning location)
# longitude, # sampling area (where fish were moved to prior to sampling, rough approximate of spawning location)
otolith_mark_present, # otolith mark present (yes = hatchery, no = natural)
otolith_mark_id, # otolith hatchery mark
otolith_mark_status_code, # otolith read status code (reason for no read = overground, lost, etc.)
silly, # GCL collection code
fish_id, # GCL fish ID within a silly
dna_tray_code, # 10-digit DWP barcode
dna_tray_well_code, # 48 DWP position 1:48
# dna_tray_well_pos, # 48 DWP position A1:H6
# distance_tide, # riverdist approximate sampling location from maximum high tide
# riverdist_seg, # riverdist river segment
# riverdist_vert, # riverdist vertex
# riverdist_snapdist, # riverdist snapping distance (aka how far was sampling location lat/long from the stream polyline)
# high_tide # highest extent of high tide in meters from mouth, unique to each stream
fw_age,
sw_age
)
)
Sample Sizes
Verify that all genotyped samples have metadata.
geno_silly_source <- sapply(sillyvec, function(silly) {get(paste0(silly, ".gcl"))$SillySource}) %>% unlist()
table(geno_silly_source %in% metadata$silly_source)
FALSE TRUE
241 18569
Shoot, which samples do not have any metadata?
Ah, I bet they are the Fish Creek tag samples from 2013 that Tyler
Dann and Heather Liller collected pre-AHRP field collections!
setdiff(geno_silly_source, metadata$silly_source)
[1] "CMFISHCRT13_1" "CMFISHCRT13_2" "CMFISHCRT13_3" "CMFISHCRT13_4" "CMFISHCRT13_5" "CMFISHCRT13_6" "CMFISHCRT13_7" "CMFISHCRT13_8"
[9] "CMFISHCRT13_9" "CMFISHCRT13_10" "CMFISHCRT13_11" "CMFISHCRT13_12" "CMFISHCRT13_13" "CMFISHCRT13_14" "CMFISHCRT13_15" "CMFISHCRT13_16"
[17] "CMFISHCRT13_17" "CMFISHCRT13_18" "CMFISHCRT13_19" "CMFISHCRT13_20" "CMFISHCRT13_21" "CMFISHCRT13_22" "CMFISHCRT13_23" "CMFISHCRT13_24"
[25] "CMFISHCRT13_25" "CMFISHCRT13_26" "CMFISHCRT13_27" "CMFISHCRT13_28" "CMFISHCRT13_29" "CMFISHCRT13_30" "CMFISHCRT13_31" "CMFISHCRT13_32"
[33] "CMFISHCRT13_33" "CMFISHCRT13_34" "CMFISHCRT13_35" "CMFISHCRT13_36" "CMFISHCRT13_37" "CMFISHCRT13_38" "CMFISHCRT13_39" "CMFISHCRT13_40"
[41] "CMFISHCRT13_41" "CMFISHCRT13_42" "CMFISHCRT13_43" "CMFISHCRT13_44" "CMFISHCRT13_45" "CMFISHCRT13_46" "CMFISHCRT13_47" "CMFISHCRT13_48"
[49] "CMFISHCRT13_49" "CMFISHCRT13_50" "CMFISHCRT13_51" "CMFISHCRT13_52" "CMFISHCRT13_53" "CMFISHCRT13_54" "CMFISHCRT13_55" "CMFISHCRT13_56"
[57] "CMFISHCRT13_57" "CMFISHCRT13_58" "CMFISHCRT13_59" "CMFISHCRT13_60" "CMFISHCRT13_61" "CMFISHCRT13_62" "CMFISHCRT13_63" "CMFISHCRT13_64"
[65] "CMFISHCRT13_65" "CMFISHCRT13_66" "CMFISHCRT13_67" "CMFISHCRT13_68" "CMFISHCRT13_69" "CMFISHCRT13_70" "CMFISHCRT13_71" "CMFISHCRT13_72"
[73] "CMFISHCRT13_73" "CMFISHCRT13_74" "CMFISHCRT13_75" "CMFISHCRT13_76" "CMFISHCRT13_77" "CMFISHCRT13_78" "CMFISHCRT13_79" "CMFISHCRT13_80"
[81] "CMFISHCRT13_81" "CMFISHCRT13_82" "CMFISHCRT13_83" "CMFISHCRT13_84" "CMFISHCRT13_85" "CMFISHCRT13_86" "CMFISHCRT13_87" "CMFISHCRT13_88"
[89] "CMFISHCRT13_89" "CMFISHCRT13_90" "CMFISHCRT13_91" "CMFISHCRT13_92" "CMFISHCRT13_93" "CMFISHCRT13_94" "CMFISHCRT13_95" "CMFISHCRT13_96"
[97] "CMFISHCRT13_97" "CMFISHCRT13_98" "CMFISHCRT13_99" "CMFISHCRT13_100" "CMFISHCRT13_101" "CMFISHCRT13_102" "CMFISHCRT13_103" "CMFISHCRT13_104"
[105] "CMFISHCRT13_105" "CMFISHCRT13_106" "CMFISHCRT13_107" "CMFISHCRT13_108" "CMFISHCRT13_109" "CMFISHCRT13_110" "CMFISHCRT13_111" "CMFISHCRT13_112"
[113] "CMFISHCRT13_113" "CMFISHCRT13_114" "CMFISHCRT13_115" "CMFISHCRT13_116" "CMFISHCRT13_117" "CMFISHCRT13_118" "CMFISHCRT13_119" "CMFISHCRT13_120"
[121] "CMFISHCRT13_121" "CMFISHCRT13_122" "CMFISHCRT13_123" "CMFISHCRT13_124" "CMFISHCRT13_125" "CMFISHCRT13_126" "CMFISHCRT13_127" "CMFISHCRT13_128"
[129] "CMFISHCRT13_129" "CMFISHCRT13_130" "CMFISHCRT13_131" "CMFISHCRT13_132" "CMFISHCRT13_133" "CMFISHCRT13_134" "CMFISHCRT13_135" "CMFISHCRT13_136"
[137] "CMFISHCRT13_137" "CMFISHCRT13_138" "CMFISHCRT13_139" "CMFISHCRT13_140" "CMFISHCRT13_141" "CMFISHCRT13_142" "CMFISHCRT13_143" "CMFISHCRT13_144"
[145] "CMFISHCRT13_145" "CMFISHCRT13_146" "CMFISHCRT13_147" "CMFISHCRT13_148" "CMFISHCRT13_149" "CMFISHCRT13_150" "CMFISHCRT13_151" "CMFISHCRT13_152"
[153] "CMFISHCRT13_153" "CMFISHCRT13_154" "CMFISHCRT13_155" "CMFISHCRT13_156" "CMFISHCRT13_157" "CMFISHCRT13_158" "CMFISHCRT13_159" "CMFISHCRT13_160"
[161] "CMFISHCRT13_161" "CMFISHCRT13_162" "CMFISHCRT13_163" "CMFISHCRT13_164" "CMFISHCRT13_165" "CMFISHCRT13_166" "CMFISHCRT13_167" "CMFISHCRT13_168"
[169] "CMFISHCRT13_169" "CMFISHCRT13_170" "CMFISHCRT13_171" "CMFISHCRT13_172" "CMFISHCRT13_173" "CMFISHCRT13_174" "CMFISHCRT13_175" "CMFISHCRT13_176"
[177] "CMFISHCRT13_177" "CMFISHCRT13_178" "CMFISHCRT13_179" "CMFISHCRT13_180" "CMFISHCRT13_181" "CMFISHCRT13_182" "CMFISHCRT13_183" "CMFISHCRT13_184"
[185] "CMFISHCRT13_185" "CMFISHCRT13_186" "CMFISHCRT13_187" "CMFISHCRT13_188" "CMFISHCRT13_189" "CMFISHCRT13_190" "CMFISHCRT13_191" "CMFISHCRT13_192"
[193] "CMFISHCRT13_193" "CMFISHCRT13_194" "CMFISHCRT13_195" "CMFISHCRT13_196" "CMFISHCRT13_197" "CMFISHCRT13_198" "CMFISHCRT13_199" "CMFISHCRT13_200"
[201] "CMFISHCRT13_201" "CMFISHCRT13_202" "CMFISHCRT13_203" "CMFISHCRT13_204" "CMFISHCRT13_205" "CMFISHCRT13_206" "CMFISHCRT13_207" "CMFISHCRT13_208"
[209] "CMFISHCRT13_209" "CMFISHCRT13_210" "CMFISHCRT13_211" "CMFISHCRT13_212" "CMFISHCRT13_213" "CMFISHCRT13_214" "CMFISHCRT13_215" "CMFISHCRT13_216"
[217] "CMFISHCRT13_217" "CMFISHCRT13_218" "CMFISHCRT13_219" "CMFISHCRT13_220" "CMFISHCRT13_221" "CMFISHCRT13_222" "CMFISHCRT13_223" "CMFISHCRT13_224"
[225] "CMFISHCRT13_225" "CMFISHCRT13_226" "CMFISHCRT13_227" "CMFISHCRT13_228" "CMFISHCRT13_229" "CMFISHCRT13_230" "CMFISHCRT13_231" "CMFISHCRT13_232"
[233] "CMFISHCRT13_233" "CMFISHCRT13_234" "CMFISHCRT13_235" "CMFISHCRT13_236" "CMFISHCRT13_237" "CMFISHCRT13_238" "CMFISHCRT13_239" "CMFISHCRT13_240"
[241] "CMFISHCRT13_241"
Correct. Add these to metadata with sample date info.
metadata <-
dplyr::bind_rows(metadata,
CMFISHCRT13.gcl %>%
dplyr::select(SILLY_CODE, FK_FISH_ID, SillySource, DNA_TRAY_CODE, DNA_TRAY_WELL_CODE, CAPTURE_DATE) %>%
dplyr::rename(silly = SILLY_CODE,
fish_id = FK_FISH_ID,
silly_source = SillySource,
dna_tray_code = DNA_TRAY_CODE,
dna_tray_well_code = DNA_TRAY_WELL_CODE,
date = CAPTURE_DATE) %>%
tidyr::unite(col = "sample", dna_tray_code:dna_tray_well_code, sep = "_", remove = FALSE) %>%
tidyr::unite(col = "silly_source", c(silly, fish_id), sep = "_", remove = FALSE) %>%
dplyr::mutate(franz_id = stringr::str_c(stringr::str_sub(silly, 1, 2), # 1st letter of species name, 1st letter of stream name
stringr::str_sub(silly, -2, -1), # 2 digit sample year
"_",
stringr::str_pad(fish_id, width = 5, pad = "0", side = "left")), # 5 digit fishid using padded zeros up front)
date = lubridate::as_date(date),
year = lubridate::year(date),
stream = "Fish - Douglas Island",
DOY = lubridate::yday(date) # day of year
)
)
Verify all samples have metadata.
table(geno_silly_source %in% metadata$silly_source)
TRUE
18810
Excellent, all present and accounted for.
Record which samples were genotyped.
metadata <- metadata %>%
dplyr::mutate(genotyped = silly_source %in% geno_silly_source)
How many samples genotyped per collection (silly)?
metadata %>%
dplyr::count(genotyped, silly) %>%
tidyr::pivot_wider(names_from = genotyped, values_from = n)
Check For Sample Duplicates
I got a weird many-to-many error in a join the first time I ran
through this script, so I want to check and see if there are actually
duplicate samples present and if so, remove them!
# bind all samples together into a single .gcl object
my.gcl <- sapply(sillyvec, function(silly) {
my.silly <- get(paste0(silly, ".gcl"))
}, simplify = FALSE) %>%
dplyr::bind_rows()
Any duplicate SillySource?
table(table(my.gcl$SillySource))
1
18810
Nope, all good there. Clean up.
Are there metadata duplicates?
table(table(metadata$silly_source))
1 2
27197 45
Well son of a biscuit, there are!
metadata %>%
dplyr::filter(duplicated(silly_source) | duplicated(silly_source, fromLast = TRUE)) %>%
dplyr::arrange(silly_source)
Okay, they appear to be true duplicates in metadata, no
conflicting columns. Remove duplicate rows.
metadata <- metadata %>%
dplyr::distinct(silly_source, .keep_all = TRUE)
Filter QC Crosscheck Plates
Create a vector of plate IDs to toss, pending QC crosschecks.
plate_ids_qcxcheck <- c(64461, 64462, 64480, 64482, 64483, 64484, 64485, 64486, 64502, 64503, 64509, 64511, 64512, 64513, 64514, 64515, 64542, 64543, 64582, 56362, 56363) %>% as.character()
Get sample size by silly.
(
sample_size_qcxcheck <- dplyr::tibble(silly = sillyvec) %>%
dplyr::mutate(genotyped = GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
Remove any fish on the plates pending QC crosschecks.
x <- sapply(sillyvec, function(silly) {
my.silly <- get(paste0(silly, ".gcl"))
my.silly <- my.silly %>% dplyr::filter(!stringr::str_detect(string = PLATE_ID, pattern = stringr::str_c(plate_ids_qcxcheck, collapse = "|")))
assign(x = paste0(silly, ".gcl"),
value = my.silly,
pos = 1)
})
rm(x) # suppresses output
How many fish were removed by silly?
(
sample_size_qcxcheck <- sample_size_qcxcheck %>%
dplyr::mutate(qcxcheck = genotyped - GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
How many total samples?
sum(sample_size_qcxcheck$qcxcheck)
[1] 1995
Okay, that looks about right for 21 plates. Must have been some
partial plates.
Genotype QA
Standard data QA (GT-seq, no conScore):
- Remove fish missing genotypes (<80% loci)
- Remove contamination (heterozygosity outliers +/-
3.5 modified z-score)
- Remove duplicates (>95% loci concordance, remove
both duplicates)
(
sample_size_qa <- dplyr::tibble(silly = sillyvec) %>%
dplyr::mutate(genotyped = GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
Missing Loci (<80%)
Remove fish with <80% loci genotyped.
miss_loci <-
GCLr::remove_ind_miss_loci(sillyvec = sillyvec,
proportion = 0.8)
G3;30 IDs were removed from CMADMCR13.gcl
gG3;30 IDs were removed from CMADMCR17.gcl
gG3;4 IDs were removed from CMADMCR18.gcl
gG3;6 IDs were removed from CMFISHCR13.gcl
gG3;110 IDs were removed from CMFISHCR14.gcl
gG3;111 IDs were removed from CMFISHCR17.gcl
gG3;79 IDs were removed from CMFISHCR18.gcl
gG3;182 IDs were removed from CMFISHCR19.gcl
gG3;6 IDs were removed from CMFISHCR21.gcl
gG3;29 IDs were removed from CMFISHCR22.gcl
gG3;36 IDs were removed from CMFISHCR23.gcl
gG3;2 IDs were removed from CMFISHCRT13.gcl
gG3;2 IDs were removed from CMFISHCRT21.gcl
gG3;3 IDs were removed from CMFISHCRT22.gcl
gG3;1 IDs were removed from CMFISHCRT23.gcl
gG3;42 IDs were removed from CMPROSCR13.gcl
gG3;5 IDs were removed from CMPROSCR14.gcl
gG3;17 IDs were removed from CMPROSCR17.gcl
gG3;8 IDs were removed from CMPROSCR18.gcl
gG3;13 IDs were removed from CMPROSCR19.gcl
gG3;2 IDs were removed from CMPROSCR21.gcl
gG3;9 IDs were removed from CMPROSCR22.gcl
gG3;2 IDs were removed from CMPROSCRT22.gcl
gG3;23 IDs were removed from CMSAWCR13.gcl
gG3;1 IDs were removed from CMSAWCR14.gcl
gG3;6 IDs were removed from CMSAWCR15.gcl
gG3;46 IDs were removed from CMSAWCR17.gcl
gG3;17 IDs were removed from CMSAWCR18.gcl
gG3;17 IDs were removed from CMSAWCR19.gcl
gG3;8 IDs were removed from CMSAWCR21.gcl
gG3;25 IDs were removed from CMSAWCR22.gcl
gG3;A total of 872 individuals were removed from sillys in sillyvec.
g
# miss_loci$IDs_Removed
GCLr::save_objects(objects = "miss_loci", path = "../objects", rds = TRUE)
(
sample_size_qa <- sample_size_qa %>%
dplyr::mutate(missing = genotyped - GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
Overall 872 samples dropped due to poor quality DNA.
What was genotyping success by spawning state?
miss_loci_tib <- tibble::enframe(miss_loci$IDs_Removed, name = "silly", value = "fish_id") %>%
tidyr::unnest_longer(fish_id) %>%
dplyr::left_join(y = metadata, by = dplyr::join_by(silly, fish_id))
metadata %>%
dplyr::filter(genotyped) %>%
dplyr::mutate(drop_miss = silly_source %in% miss_loci_tib$silly_source) %>%
dplyr::count(drop_miss, spawning_state) %>%
tidyr::pivot_wider(names_from = drop_miss, values_from = n) %>%
dplyr::rename(n_drop = `TRUE`, n_keep = `FALSE`) %>%
dplyr::mutate(n = n_keep + n_drop,
p_keep = n_keep / n,
spawning_state = factor(spawning_state, levels = c("Alive", "Pink Gill", "Grey Gill", "Rotting"))) %>%
dplyr::arrange(spawning_state)
As expected, genotyping success was lower for rotting fish, but
honestly, not terrible!
Contamination (Heterozygosity > +/- 3.5 modified Z-score)
Remove contaminated samples prior to checking for duplicates!
Individual heterozygosity outliers is our best metric for removing
contaminated samples in the absence of something more sophisticated like
conScore from GTscore.
In previous analyses, including Shedd et al. 2022, we’ve
used the standard 1.5 IQR outlier detection method to remove excessively
heterozygous individuals. However, the 1.5 IQR method assumes a normal
distribution, but heart sample heterozygosities tend to be skewed right
due to contamination. As of 2024-02-08, we decided to pivot to using modified
Z-scores with cutoffs of +/- 3.5 as recommended by Iglewicz and
Hoaglin . The modified Z-score uses the median and
median absolute deviation (MAD) instead of the mean and standard
deviation, and is thus more robust to outliers and asymmetrical
distributions.
Source function and calculate individual heterozygosities from
AHRP-PWS-Pink-Salmon-Pedigrees GitHub repo.
source("~/GitHub_repos/AHRP-PWS-Pink-Salmon-Pedigrees/code/functions/calc_ind_het.R")
(
ind_het <-
calc_ind_het(
sillyvec = sillyvec,
loci = loci251,
ncores = parallel::detectCores() - 4
)
)
Time difference of 17.24236 secs
Calculate +/- 3.5 modified z-score cutoffs by stream.
NOTE this means that the exact heterozygosity cutoff
will vary slightly by stream. For PWS Pink Salmon, I did it separately
by silly, but some of our sample sizes are pretty small here and we have
the separate “tag” sillys. I’m opting to do this by stream (all years
together) to allow for subtle changes in allele frequency per
stream.
# Function to calculate median absolute deviation (MAD)
mad <- function(x) {
median(abs(x - median(x)))
}
# Calculate the modified z-score for each individual
(
ind_het <- ind_het %>%
dplyr::mutate(
year = 2000 + as.numeric(stringr::str_sub(
string = silly,
start = -2,
end = -1
)),
stream = dplyr::case_when(
stringr::str_detect(string = silly, pattern = "ADM") ~ "Admiralty",
stringr::str_detect(string = silly, pattern = "FISH") ~ "Fish",
stringr::str_detect(string = silly, pattern = "PROS") ~ "Prospect",
stringr::str_detect(string = silly, pattern = "SAW") ~ "Sawmill",
TRUE ~ "mistakes_were_made"
)
) %>%
dplyr::group_by(stream) %>% # cutoffs are specific to each stream!
dplyr::mutate(
modified_z_score = 0.6745 * (het - median(het)) / mad(het),
outlier = dplyr::case_when(abs(modified_z_score) > 3.5 ~ TRUE, TRUE ~ FALSE)
) %>%
dplyr::ungroup()
)
GCLr::save_objects("ind_het", path = "objects", rds = TRUE)
Plot distribution of heterozygosity and show outliers.
ind_het %>%
ggplot2::ggplot(ggplot2::aes(x = het, fill = outlier)) +
ggplot2::geom_histogram(binwidth = 1 / length(loci251)) +
ggplot2::facet_grid(rows = ggplot2::vars(stream), scales = "free_y") +
ggplot2::xlim(0, 1) +
ggplot2::scale_fill_manual(name = "Outlier", values = c("TRUE" = "black", "FALSE" = "grey60")) +
ggplot2::xlab("Individual Heterozygosity") +
ggplot2::ylab("Frequency") +
ggplot2::ggtitle("Individual Heterozygosity - By Stream") +
ggplot2::theme_bw(base_size = 14)

Remove outliers.
output <- lapply(sillyvec, function(x) {
GCLr::remove_ids(
silly = x,
IDs = ind_het %>% dplyr::filter(outlier, silly == x) %>% dplyr::pull(fish_id)
)
})
G3;8 IDs were removed from CMADMCR13.gcl
gG3;5 IDs were removed from CMADMCR14.gcl
gG3;147 IDs were removed from CMADMCR17.gcl
gG3;2 IDs were removed from CMADMCR18.gcl
gG3;11 IDs were removed from CMFISHCR13.gcl
gG3;113 IDs were removed from CMFISHCR14.gcl
gG3;111 IDs were removed from CMFISHCR17.gcl
gG3;49 IDs were removed from CMFISHCR18.gcl
gG3;55 IDs were removed from CMFISHCR19.gcl
gG3;2 IDs were removed from CMFISHCR20.gcl
gG3;7 IDs were removed from CMFISHCR21.gcl
gG3;17 IDs were removed from CMFISHCR22.gcl
gG3;100 IDs were removed from CMFISHCR23.gcl
gG3;1 IDs were removed from CMFISHCRT13.gcl
gG3;0 IDs were removed from CMFISHCRT21.gcl
gG3;1 IDs were removed from CMFISHCRT22.gcl
gG3;3 IDs were removed from CMFISHCRT23.gcl
gG3;18 IDs were removed from CMPROSCR13.gcl
gG3;10 IDs were removed from CMPROSCR14.gcl
gG3;153 IDs were removed from CMPROSCR17.gcl
gG3;6 IDs were removed from CMPROSCR18.gcl
gG3;50 IDs were removed from CMPROSCR19.gcl
gG3;0 IDs were removed from CMPROSCR20.gcl
gG3;2 IDs were removed from CMPROSCR21.gcl
gG3;10 IDs were removed from CMPROSCR22.gcl
gG3;0 IDs were removed from CMPROSCRT21.gcl
gG3;0 IDs were removed from CMPROSCRT22.gcl
gG3;17 IDs were removed from CMSAWCR13.gcl
gG3;2 IDs were removed from CMSAWCR14.gcl
gG3;4 IDs were removed from CMSAWCR15.gcl
gG3;93 IDs were removed from CMSAWCR17.gcl
gG3;1 IDs were removed from CMSAWCR18.gcl
gG3;0 IDs were removed from CMSAWCR19.gcl
gG3;0 IDs were removed from CMSAWCR20.gcl
gG3;2 IDs were removed from CMSAWCR21.gcl
gG3;0 IDs were removed from CMSAWCR22.gcl
gG3;0 IDs were removed from CMSAWCRT21.gcl
gG3;0 IDs were removed from CMSAWCRT22.gcl
g
message(paste0("A total of ", ind_het %>% dplyr::filter(outlier) %>% nrow(), " individuals were removed from sillys in sillyvec."))
G3;A total of 1000 individuals were removed from sillys in sillyvec.
g
Not too many contaminated samples! Most likely culled during missing
loci.
(
sample_size_qa <- sample_size_qa %>%
dplyr::mutate(heterozygosity = genotyped - missing - GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n))
)
Overall 1,000 samples dropped due to sample contamination.
What was contamination by spawning state?
metadata %>%
dplyr::filter(genotyped) %>%
dplyr::mutate(het_outlier = silly_source %in% (ind_het %>% dplyr::filter(outlier) %>% dplyr::pull(SillySource))) %>%
dplyr::count(het_outlier, spawning_state) %>%
tidyr::pivot_wider(names_from = het_outlier, values_from = n) %>%
dplyr::rename(n_drop = `TRUE`, n_keep = `FALSE`) %>%
dplyr::mutate(n = n_keep + n_drop,
p_keep = n_keep / n,
spawning_state = factor(spawning_state, levels = c("Alive", "Pink Gill", "Grey Gill", "Rotting"))) %>%
dplyr::arrange(spawning_state)
As expected, contamination (heterozygosity outliers) was higher for
rotting fish, but honestly, not terrible!
Duplicate (>=95%)
Identify potential duplicate genotypes (>=95% loci).
duplicate_check_95 <-
GCLr::dupcheck_within_silly(
sillyvec = sillyvec,
minproportion = 0.95,
minnonmissing = 0.6,
ncores = parallel::detectCores() - 4
)
Time difference of 21.62875 secs
GCLr::save_objects("duplicate_check_95", path = "objects", rds = TRUE)
How many duplicate pairs by silly?
duplicate_check_95 %>%
dplyr::count(silly) %>%
dplyr::arrange(dplyr::desc(n))
Remove both duplicates! As opposed to GSI work,
where we want to keep individuals but aren’t typically worried about
paired data, here we want to remove both individuals as the paired data
integrity (including sample date, otolith reads, etc.) is lost.
duplicates_removed <-
GCLr::remove_dups(dupcheck = duplicate_check_95, remove_both = TRUE)
G3;2 IDs were removed from CMADMCR14.gcl
gG3;20 IDs were removed from CMADMCR17.gcl
gG3;6 IDs were removed from CMADMCR18.gcl
gG3;2 IDs were removed from CMFISHCR13.gcl
gG3;12 IDs were removed from CMFISHCR14.gcl
gG3;14 IDs were removed from CMFISHCR17.gcl
gG3;38 IDs were removed from CMFISHCR18.gcl
gG3;18 IDs were removed from CMFISHCR19.gcl
gG3;4 IDs were removed from CMFISHCR23.gcl
gG3;2 IDs were removed from CMFISHCRT23.gcl
gG3;2 IDs were removed from CMPROSCR13.gcl
gG3;4 IDs were removed from CMPROSCR14.gcl
gG3;14 IDs were removed from CMPROSCR17.gcl
gG3;4 IDs were removed from CMPROSCR18.gcl
gG3;10 IDs were removed from CMPROSCR19.gcl
gG3;4 IDs were removed from CMPROSCR20.gcl
gG3;2 IDs were removed from CMPROSCR22.gcl
gG3;8 IDs were removed from CMSAWCR17.gcl
gG3;4 IDs were removed from CMSAWCR18.gcl
gG3;2 IDs were removed from CMSAWCR22.gcl
g
GCLr::save_objects("duplicates_removed", path = "objects", rds = TRUE)
(
sample_size_qa <- sample_size_qa %>%
dplyr::mutate(
duplicate = genotyped - missing - heterozygosity - GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n)
)
)
Overall 157 samples dropped due to duplicate genotypes.
Final
Final, post-QA sample sizes by silly.
(
sample_size_qa <- sample_size_qa %>%
dplyr::mutate(
final = GCLr::silly_n(sillyvec = sillyvec) %>% dplyr::pull(n)
)
)
GCLr::save_objects("sample_size_qa", path = "objects", rds = TRUE)
fs::dir_create("output")
readr::write_csv(x = sample_size_qa, file = "output/sample_size_qa.csv")
Save
Save final genotypes and update metadata.
Genotypes
Save post-QA genotypes with joined metadata.
fs::dir_create("data/genotypes/postQA")
GCLr::save_sillys(sillyvec = sillyvec, path = "data/genotypes/postQA", rds = TRUE)
QA Conclusions
Overall 18,810 total pedigree samples were genotyped; 1,995 were
removed due to pending QC Crosschecks; 872 were removed due to poor
quality DNA (missing >= 20% loci); 1,000 were removed due to
contaminated DNA (heterozygosity >= 3.5 modified z-score); and 157
were removed due to duplicate genotypes (>= 95% loci), leaving a
total of 14,786 in the final dataset.
Adaptive Loci Genotypes
Create a single object with just the metadata and adaptive loci
genotypes for NOAA ABL.
Calculate Allele Frequencies
I’m going to look at this three different ways:
- naive, all samples (including hatchery strays) with collection
date,
- natural-origin fish only (no hatchery strays) with collection date,
and
- natural-origin fish only (no hatchery strays) with adjusted
collection date based on
spawning_state (re-scaling to
Alive) * Alive = collection date * Pink Gill = collection date
- 2 days * Grey Gill = collection date - 5 days * Rotting = collection
date - 10 days
Stream sampling was limited by both crew availability and weather
conditions (flooding), so collection date is not a perfect proxy for run
timing. Additionally, in some of the later years crews were tagging live
fish near the intertidal to boost genetic sample size; so these live
fish were sampled much earlier than post-spawn carcasses with the same
run timing.
All Samples (Hatchery + Natural)
Calculate the allele counts for each locus by stream, year, and DOY.
Adapted from Pat Barry’s code in AHRP_ChumAdaptive.html
loci_adaptive <- grep(pattern = "OkeV2", x = loci251, value = TRUE)
GCLr::save_objects(objects = "loci_adaptive", path = "objects/")
# testing
# chum_adaptive_geno %>%
# dplyr::select(silly_source, stream, year, DOY, tidyselect::starts_with(loci_adaptive[1])) %>%
# tidyr::pivot_longer(cols = tidyselect::starts_with(loci_adaptive[1]), names_to = "locus", values_to = "allele") %>%
# dplyr::mutate(allele = factor(allele, levels = LocusControl$alleles[[loci_adaptive[1]]]$call),
# allele = as.numeric(allele)) %>% # make factor and convert to numeric
# dplyr::count(stream, year, DOY, allele) %>%
# dplyr::mutate(locus = loci_adaptive[1])
# calculate the number of each allele observed per stream, year, day
# NOTE: there is no filtering for hatchery/natural origin
(chum_adaptive_allele_counts <- lapply(loci_adaptive, function(locus) {
chum_adaptive_geno %>%
dplyr::select(silly_source,
stream,
year,
DOY,
tidyselect::starts_with(locus)) %>%
tidyr::pivot_longer(
cols = tidyselect::starts_with(locus),
names_to = "locus",
values_to = "allele"
) %>%
dplyr::mutate(allele = factor(allele, levels = LocusControl$alleles[[{{locus}}]]$call), # order alleles as per LocusControl
allele = as.numeric(allele)) %>% # make factor and convert to numeric
dplyr::count(stream, year, DOY, allele) %>%
dplyr::mutate(locus = locus)
}) %>%
dplyr::bind_rows())
Convert to allele frequencies.
(
chum_adaptive_allele_freq <- chum_adaptive_allele_counts %>%
tidyr::pivot_wider(names_from = allele, values_from = n) %>%
dplyr::select(-`NA`) %>% # drop no-calls
dplyr::mutate(
`1` = replace_na(`1`, 0),
# account for unobserved alleles on a given day
`2` = replace_na(`2`, 0)
) %>%
tidyr::pivot_longer(
cols = `1`:`2`,
names_to = "allele",
values_to = "n"
) %>%
dplyr::group_by(stream, year, DOY, locus) %>%
dplyr::mutate(
n_alleles = sum(n),
p_alleles = n / n_alleles,
SNP = paste(locus, allele, sep = "_")
) %>%
dplyr::ungroup() %>%
dplyr::filter(n_alleles > 0) # remove any stream/year/DOY/locus combinations without data
)
Confirm data structure.
chum_adaptive_allele_freq %>%
dplyr::group_by(locus) %>%
dplyr::summarise(min(p_alleles),
max(p_alleles))
chum_adaptive_allele_freq %>%
dplyr::filter(is.na(p_alleles))
Natural-origin Samples (no Hatchery Strays)
Re-calculate the allele counts for natural-origin
fish only for each locus by stream, year, and DOY. No hatchery strays!
Adapted from Pat Barry’s code in AHRP_ChumAdaptive.html
# calculate the number of each allele observed per stream, year, day
# NOTE: removing all hatchery-origin strays!!!
(chum_adaptive_allele_counts_wild <- lapply(loci_adaptive, function(locus) {
chum_adaptive_geno %>%
dplyr::filter(origin == "Natural") %>% # no hatchery strays or unknown origin fish
dplyr::select(silly_source,
stream,
year,
DOY,
tidyselect::starts_with(locus)) %>%
tidyr::pivot_longer(
cols = tidyselect::starts_with(locus),
names_to = "locus",
values_to = "allele"
) %>%
dplyr::mutate(allele = factor(allele, levels = LocusControl$alleles[[{{locus}}]]$call), # order alleles as per LocusControl
allele = as.numeric(allele)) %>% # make factor and convert to numeric
dplyr::count(stream, year, DOY, allele) %>%
dplyr::mutate(locus = locus)
}) %>%
dplyr::bind_rows())
Convert to allele frequencies.
(
chum_adaptive_allele_freq_wild <- chum_adaptive_allele_counts_wild %>%
tidyr::pivot_wider(names_from = allele, values_from = n) %>%
dplyr::select(-`NA`) %>% # drop no-calls
dplyr::mutate(
`1` = replace_na(`1`, 0),
# account for unobserved alleles on a given day
`2` = replace_na(`2`, 0)
) %>%
tidyr::pivot_longer(
cols = `1`:`2`,
names_to = "allele",
values_to = "n"
) %>%
dplyr::group_by(stream, year, DOY, locus) %>%
dplyr::mutate(
n_alleles = sum(n),
p_alleles = n / n_alleles,
SNP = paste(locus, allele, sep = "_")
) %>%
dplyr::ungroup() %>%
dplyr::filter(n_alleles > 0) # remove any stream/year/DOY/locus combinations without data
)
Adjust for Spawning State
Re-calculate the allele counts for natural-origin
fish only for each locus by stream, year, and DOY. No hatchery strays!
Adjust DOY based on spawning state, re-scaling to Alive.
Adjustments are verrrry rough guesses.
- Rotting = Alive + 10 days
- Grey Gill = Alive + 5 days
- Pink Gill = Alive + 2 days
Adapted from Pat Barry’s code in
AHRP_ChumAdaptive.html
# calculate the number of each allele observed per stream, year, day
# NOTE: removing all hatchery-origin strays!!!
(chum_adaptive_allele_counts_wild_adj_spawning <- lapply(loci_adaptive, function(locus) {
chum_adaptive_geno %>%
dplyr::filter(origin == "Natural") %>% # no hatchery strays or unknown origin fish
dplyr::mutate(DOY = dplyr::case_when(spawning_state == "Pink Gill" ~ DOY - 2,
spawning_state == "Grey Gill" ~ DOY - 5,
spawning_state == "Rotting" ~ DOY - 10,
TRUE ~ DOY
)) %>% # adjust DOY based on spawning date, adjustments are complete WAGs!!!
dplyr::select(silly_source,
stream,
year,
DOY,
tidyselect::starts_with(locus)) %>%
tidyr::pivot_longer(
cols = tidyselect::starts_with(locus),
names_to = "locus",
values_to = "allele"
) %>%
dplyr::mutate(allele = factor(allele, levels = LocusControl$alleles[[{{locus}}]]$call), # order alleles as per LocusControl
allele = as.numeric(allele)) %>% # make factor and convert to numeric
dplyr::count(stream, year, DOY, allele) %>%
dplyr::mutate(locus = locus)
}) %>%
dplyr::bind_rows())
Convert to allele frequencies.
(
chum_adaptive_allele_freq_wild_adj_spawning <- chum_adaptive_allele_counts_wild_adj_spawning %>%
tidyr::pivot_wider(names_from = allele, values_from = n) %>%
dplyr::select(-`NA`) %>% # drop no-calls
dplyr::mutate(
`1` = replace_na(`1`, 0),
# account for unobserved alleles on a given day
`2` = replace_na(`2`, 0)
) %>%
tidyr::pivot_longer(
cols = `1`:`2`,
names_to = "allele",
values_to = "n"
) %>%
dplyr::group_by(stream, year, DOY, locus) %>%
dplyr::mutate(
n_alleles = sum(n),
p_alleles = n / n_alleles,
SNP = paste(locus, allele, sep = "_")
) %>%
dplyr::ungroup() %>%
dplyr::filter(n_alleles > 0) # remove any stream/year/DOY/locus combinations without data
)
View Allele Frequencies
Create functions to standardize plots. Each set of plots is facetted
to show years/loci. NOTE since I am forcing the y-axes
to 0-1, the SE ribbons get cut off for some of the fit lines.
adaptive_freq_plot <- function(freq, stream) {
freq %>%
dplyr::filter(stream == {{stream}}) %>%
dplyr::mutate(locus = stringr::str_remove_all(string = locus, pattern = "OkeV2_")) %>% # shorten locus names to fit
ggplot2::ggplot(ggplot2::aes(x = DOY, y = p_alleles, colour = SNP, group_by(SNP))) +
ggplot2::geom_point() +
ggplot2::geom_smooth(method = "lm", weight = "n_alleles", formula = y~x) +
ggplot2::ylim(0, 1) + # this cuts off the SE's for the fit line
ggplot2::facet_grid(locus ~ year) +
ggplot2::theme_bw() +
ggplot2::theme(legend.position = 'none',
axis.text.x = ggplot2::element_text(angle = 90, vjust = 0.5, hjust = 0.5)) +
ggplot2::labs(title = stream, x = "DOY", y = "Allele Frequency")
}
avg_allele_n_plot <- function(freq, stream) {
freq %>%
dplyr::filter(stream == {{stream}}) %>%
dplyr::group_by(stream, year, DOY) %>%
dplyr::summarise(n_alleles = round(mean(n_alleles)), .groups = "drop") %>%
ggplot2::ggplot(ggplot2::aes(x = DOY, y = n_alleles)) +
ggplot2::geom_col() +
ggplot2::facet_grid(~year) +
ggplot2::labs(title = paste0(stream, " - Average Allele Sample Size per Day"), y = "Average Number of Alleles") +
ggplot2::theme_bw() +
ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 90, vjust = 0.5, hjust = 0.5))
}
All Samples
Includes hatchery-origin strays and unknown origin fish (i.e. no
otolith read).
Admiralty
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq, stream = "Admiralty")

NOTE small sample sizes in 2018 and later sampling
dates due to flooding conditions.
adaptive_freq_plot(freq = chum_adaptive_allele_freq, stream = "Admiralty")

Inconsistent patterns among years for most loci. The most consistent
relationship is in LG29_25515161. Some evidence of a brood
year effect with more similarities between 2013/2017 (assuming most chum
are age 4).
Fish
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq, stream = "Fish - Douglas Island")

adaptive_freq_plot(freq = chum_adaptive_allele_freq %>% dplyr::filter(year != 2020), stream = "Fish - Douglas Island") # ditch 2020

Inconsistent patterns among years for LG35 loci, but LG29 loci are
reasonably consistent.
Prospect
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq, stream = "Prospect")

NOTE sample sizes are lackluster for 2020 and 2021,
as are the tails of the run in 2018.
adaptive_freq_plot(freq = chum_adaptive_allele_freq, stream = "Prospect")

Inconsistent patterns among years for LG29 loci (see
LG29_25455833 for 2014 vs. 2019!), but LG35 loci are
reasonably consistent.
Sawmill
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq, stream = "Sawmill")

NOTE sample sizes reasonable for all except
2020.
adaptive_freq_plot(freq = chum_adaptive_allele_freq %>% dplyr::filter(year != 2020), stream = "Sawmill") # ditch 2020; low n

More similar to Fish Creek - Douglas Island. Weak, inconsistent
relationship for LG35 loci, but LG29 loci have a consistent relationship
across years.
Natural-origin
Only includes natural-origin fish.
Admiralty
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq_wild, stream = "Admiralty")

Very few hatchery strays into Admiralty, no big changes in sample
sizes.
adaptive_freq_plot(freq = chum_adaptive_allele_freq_wild, stream = "Admiralty")

Similar patterns as noted above.
Fish
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq_wild %>% dplyr::filter(year != 2020), stream = "Fish - Douglas Island") # ditch 2020; low n

Lot of hatchery strays in Fish Creek - Douglas Island! Good sample
sizes throughout the run for most years, excluding 2013 (sparse
sampling) and 2021 (low n).
adaptive_freq_plot(freq = chum_adaptive_allele_freq_wild %>% dplyr::filter(year != 2020), stream = "Fish - Douglas Island") # ditch 2020; low n

Some subtle changes in relationships when excluding hatchery strays,
but general pattern of stronger relationship with LG29 loci holds.
Prospect
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq_wild, stream = "Prospect")

NOTE sample sizes remain lackluster for 2020 and
2021, as are the tails of the run in 2018.
adaptive_freq_plot(freq = chum_adaptive_allele_freq_wild, stream = "Prospect")

Minimal changes to relationships after excluding hatchery strays.
Sawmill
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq_wild, stream = "Sawmill")

NOTE sample sizes reasonable for all except
2020.
adaptive_freq_plot(freq = chum_adaptive_allele_freq_wild %>% dplyr::filter(year != 2020), stream = "Sawmill") # ditch 2020

The pattern of stronger relationships with LG29 loci holds.
Natural-origin + Adjust for Spawning State
Only includes natural-origin fish and adjustments to collection date
(DOY) based on spawning state (re-scaled to Alive).
Admiralty
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq_wild_adj_spawning, stream = "Admiralty")

Distribution changes a bit due to spawning_state
adjustment to DOY.
adaptive_freq_plot(freq = chum_adaptive_allele_freq_wild_adj_spawning, stream = "Admiralty")

Adjusting DOY for spawning_state doesn’t appear to alter
relationships much for Admiralty. LG29 loci have a stronger association
with run timing than LG35.
Fish
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq_wild_adj_spawning %>% dplyr::filter(year != 2020), stream = "Fish - Douglas Island") # ditch 2020; low n

Lot of hatchery strays in Fish Creek - Douglas Island! Good sample
sizes throughout the run for most years, excluding 2013 (sparse
sampling) and 2021 (low n).
adaptive_freq_plot(freq = chum_adaptive_allele_freq_wild_adj_spawning %>% dplyr::filter(year != 2020), stream = "Fish - Douglas Island") # ditch 2020

Adjusting DOY for spawning_state definitely introduces
some more noise, but LG29 loci still have a stronger association with
run timing than LG35.
Prospect
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq_wild_adj_spawning, stream = "Prospect")

NOTE bigger shift in the distribution of DOY for
2013 and 2019 than other years.
adaptive_freq_plot(freq = chum_adaptive_allele_freq_wild_adj_spawning, stream = "Prospect")

Adjusting DOY for spawning_state definitely introduces
some more noise, but cleans up the relationships with LG29 loci (except
for LG29_25455833, dunno what is going on with those brood
year effects!).
Sawmill
Show average allele sample sizes per DOY by year.
avg_allele_n_plot(freq = chum_adaptive_allele_freq_wild_adj_spawning, stream = "Sawmill")

NOTE sample sizes getting fairly small per DOY, but
good distribution.
adaptive_freq_plot(freq = chum_adaptive_allele_freq_wild_adj_spawning %>% dplyr::filter(year != 2020), stream = "Sawmill") # ditch 2020

Adjusting DOY for spawning_state appears to have
reversed the relationships for 2019 for LG29_2540752 and
LG_25483595?
Export Genotypes for NOAA ABL
Save as both a .csv and .rds.
readr::write_csv(x = chum_adaptive_geno, file = "output/chum_adaptive_geno_2025-07-30.csv")
GCLr::save_objects(objects = "chum_adaptive_geno", path = "output", rds = TRUE)
Conclusions
Nine adaptive loci associated with run timing were identified by NOAA
ABL and included in the ADF&G AHRP chum salmon parentage GT-seq
panel. ~19K SEAK chum AHRP pedigree samples were genotyped at the GCL.
~4K samples were removed from further analyses due to pending QC cross
checks, poor quality DNA, contaminated samples, or duplicate genotypes,
leaving a total of ~15K samples collected from 2013-2023. Weighted
linear regressions were performed to investigate the association between
adaptive loci allele frequencies and run timing (collection date) using
code adapted from Pat Barry (NOAA ABL). Note that collection date is an
imperfect proxy of run timing for these fish given that samples were
collected from both living fish and post-spawn carcasses! Adjusting
collection date for fish condition (spawning_state) appears
to have improved some relationships. The strength of association appears
to vary both by stream, year, and origin (hatchery vs. natural), with
some apparent brood year effects (assuming most chum are age-4). The
sole LG23 SNP shows inconsistent and at times contrasting patterns
across years, whereas SNPs on LG29 (ESRB) and LG35 (LRRC9) show more
consistency across streams and years. LG29 loci (except
LG_25455833) were more consistently associated with run
timing than LG35 loci in all streams after adjusting DOY to account for
spawning_state.
end
LS0tDQp0aXRsZTogIkV4cG9ydCBHZW5vdHlwZXMgZm9yIDkgQWRhcHRpdmUgUnVuIFRpbWluZyBMb2NpIHRvIE5PQUEgQUJMIg0Kc3VidGl0bGU6ICJGaWx0ZXJlZCBmb3IgUGxhdGVzIE5vdCBOZWVkaW5nIFFDIENyb3NzY2hlY2tzIg0KYXV0aG9yOiAiS3lsZSBTaGVkZCINCmRhdGU6ICJTdGFydGVkOiAyMDI1LTA3LTI0LCBsYXN0IG9wZW5lZDogYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdGhlbWU6IHVuaXRlZA0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB0cnVlDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQojIFNldHVwDQoNCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCnJtKGxpc3QgPSBscyhhbGwubmFtZXMgPSBUUlVFKSkNCg0KaWYoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKTsgbGlicmFyeShwYWNtYW4pDQoNCnBhY21hbjo6cF9sb2FkKA0KICB0aWR5dmVyc2UsDQogIHNjYWxlcywNCiAgamFuaXRvciwNCiAgR0NMciwNCiAgY29pbg0KKQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTApDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQoNCi51c2VybmFtZSA9IHJlYWRMaW5lcygifi91c3JfcHcudHh0IiwgbiA9IDEpICAjIExPS0kgdXNlcm5hbWUNCi5wYXNzd29yZCA9IHJlYWRMaW5lcygifi91c3JfcHcudHh0IiAsIG4gPSAyKVtbMl1dICAjIExPS0kgcGFzc3dvcmQNCmBgYA0KDQojIE9iamVjdGl2ZQ0KDQpUaGUgcHVycG9zZSBvZiB0aGlzIG5vdGVib29rIGlzIHRvIHByb3ZpZGUgV2VzIExhcnNvbiAoTk9BQSBBQkwpIHdpdGggYWRhcHRpdmUgcnVuIHRpbWluZyBnZW5vdHlwZXMgKDkgbG9jaSkgZm9yIEFIUlAgY2h1bSBzYW1wbGVzLiBGaWx0ZXIgb3V0IHBsYXRlcyB1bmRlcmdvaW5nIFFDIGNyb3NzY2hlY2tzIGR1ZSB0byBwb29yIGdlbm90eXBpbmcgc3VjY2Vzcy4gV2VzIGlzIGludGVyZXN0ZWQgaW4gcmVwcmVzZW50YXRpb24gYWNyb3NzIHllYXJzLCBsZXNzIGFjcm9zcyBtdWx0aXBsZSBzdHJlYW1zLiBIZSB3YW50cyB0byBzZWUgaG93IHRoZSBwYXR0ZXJucyBob2xkIHVwIG92ZXIgdGltZS4gTWlnaHQgYXMgd2VsbCBzZW5kIGV2ZXJ5dGhpbmcgd2UgaGF2ZSB0aG91Z2ghDQoNCiMjIFN0ZXBzDQoNCiAgKiBpbXBvcnQgYWxsIGV4aXN0aW5nIGdlbm90eXBlcyBmb3IgQ00wNjQtQ00wNzANCiAgKiBmaWx0ZXIgb3V0IHVudHJ1c3R3b3J0aHkgcGxhdGVzIHRoYXQgbmVlZCBRQyBjcm9zcyBjaGVja3MNCiAgKiBzdGFuZGFyZCBHVC1zZXEgZ2Vub3R5cGUgUUEgKG1pc3NpbmcsIGhldGVyb3p5Z29zaXR5LCBkdXBsaWNhdGUpDQogICogam9pbiBzYW1wbGUgZGF0ZSBhbmQgc3Bhd25pbmcgc3RhdGUgPCIuLi9PY2VhbkFLL0FIUlAgU2FsbW9uIEJpb2xvZ2ljYWwgRGF0YSAyMDI0MDEwNV8xMjI0MDQuMzY4NDU5X3dpdGhfc3Bhd25pbmdfc3RhdGUuY3N2Ij4NCiAgKiBmaWx0ZXIgZm9yIDkgYWRhcHRpdmUgcnVuIHRpbWluZyBsb2NpDQogICogZXhwb3J0IGdlbm90eXBlcyB3aXRoIGRhdGUvc3Bhd25pbmcgc3RhdGUNCg0KIyBCYWNrZ3JvdW5kDQoNCkFuIGFkYXB0aXZlIG1hcmtlciBwYW5lbCBhc3NvY2lhdGVkIHdpdGggcnVuIHRpbWluZyB2YXJpYXRpb24gaW4gY2h1bSBzYWxtb24gd2FzIGRldmVsb3BlZCBieSBOT0FBIEFCTCBhbmQgdXNlZCB0byBnZW5vdHlwZSB0d28gcGxhdGVzIG9mIHNhbXBsZXMgZnJvbSB0aGUgQURGRyBBbGFza2EgSGF0Y2hlcnkgUmVzZWFyY2ggUHJvZ3JhbSAoQUhSUCkgc3R1ZHkgYmVpbmcgY29uZHVjdGVkIHdpdGhpbiBTRSBBbGFza2EuIEFIUlAgc2FtcGxlcyAoRmlzaCBDcmVlayAtIERvdWdsYXMgSXNsYW5kIDIwMjMgKyBQcm9zcGVjdCBDcmVlayAyMDE3LzIwMTgpIHdlcmUgZ2Vub3R5cGVkIGluIEF1Z3VzdCAyMDI0IGZvciAyMCBzaW5nbGUgbnVjbGVvdGlkZSBwb2x5bW9ycGhpc21zIGFuZCB3ZWlnaHRlZCBsaW5lYXIgcmVncmVzc2lvbnMgd2VyZSBwZXJmb3JtZWQgdG8gYXNzZXNzIHRoZSBhc3NvY2lhdGlvbiBvZiBkaWZmZXJlbnQgU05QcyB3aXRoIHJ1biB0aW1pbmcgKGNvbGxlY3Rpb24gZGF0ZSkuIFRoZSBjaHVtIHNhbG1vbiBhZGFwdGl2ZSBtYXJrZXIgcGFuZWwgY29udGFpbnMgMjAgbG9jaSBzcHJlYWQgYW1vbmcgNSBjaHJvbW9zb21lcyBhbmQgd2FzIGRldmVsb3BlZCBmcm9tIHZhcmlhdGlvbiBkZXRlY3RlZCBiZXR3ZWVuIFl1a29uIFJpdmVyIHN1bW1lciAoR2lzYXNhIFJpdmVyKSBhbmQgZmFsbCAoUGVsbHkgUml2ZXIpIHJ1biBjaHVtIHNhbG1vbiB3aXRoIGxvdyBjb3ZlcmFnZSB3aG9sZSBnZW5vbWUgc2VxdWVuY2luZy4gVGhlc2UgcG9wdWxhdGlvbnMgc3Bhd24gYXBwcm94aW1hdGVseSB0d28gbW9udGhzIGFuZCBvdmVyIDE1MDAgcml2ZXIga20gYXBhcnQuIE5PQUEgdGFyZ2V0ZWQgdGhlIG1ham9yIHBlYWtzIGluIGRpZmZlcmVudGlhdGlvbiB3aXRoIHBhcnRpY3VsYXIgaW50ZXJlc3QgaW4gbWFya2VycyB3aXRoaW4gdGhlIGdlbmVzIGxldWNpbmUgcmljaCByZXBlYXQgY29udGFpbmluZyBuaW5lIChMUlJDOTsgTEczNSkgYW5kIGVzdHJvZ2VuIHJlY2VwdG9yIM6yIChFU1JCOyBMRzI5KSBhcyB0aGV5IHdlcmUgYXNzb2NpYXRlZCB3aXRoIHJldHVybiB0aW1pbmcgaW4gb3RoZXIgc2FsbW9uaWQgc3BlY2llcy4gRWlnaHQgU05QcyAoZm91ciBvbiBMRzI5IGFuZCBmb3VyIG9uIExHMzUpIGFwcGVhciB0byBiZSBhc3NvY2lhdGVkIHdpdGggcnVuIHRpbWluZyB3aXRoaW4gdGhlc2UgY29sbGVjdGlvbnMuIFRoZSBzdHJlbmd0aCBvZiBhc3NvY2lhdGlvbiBmb3IgQUhSUCBzYW1wbGVzIGFwcGVhcnMgdG8gdmFyeSBieSBwb3B1bGF0aW9uLiBObyBtYXJrZXJzIG9uIExHcyA1LCAyMyBvciAxNSBhcHBlYXIgdG8gaGF2ZSBhIHN0cm9uZyBhc3NvY2lhdGlvbiB3aXRoIHJ1biB0aW1pbmcgd2l0aGluIHRoZXNlIGNvbGxlY3Rpb25zLiBPdmVyYWxsLCB0aGUgcmVzdWx0cyBmcm9tIExHMjkgYW5kIExHMzUgYXJlIHByb21pc2luZyBhbmQgdmFyaWF0aW9uIGluIHRoZXNlIFNOUHMgc2hvdWxkIGJlIHN1cnZleWVkIGZvciBhIGxhcmdlciBjb2xsZWN0aW9uIG9mIHNhbXBsZXMuDQoNCkEgdG90YWwgb2YgOSBhZGFwdGl2ZSwgcnVuIHRpbWluZyBsb2NpIHdlcmUgYWRkZWQgdG8gdGhlIEFIUlAgU0VBSyBDaHVtIHBhcmVudGFnZSBwYW5lbDogDQoNCiAgKiBPa2VWMl9MRzIzXzEwNTQ2MjM3DQogICogT2tlVjJfTEcyOV8yNTQ1MDc1Mg0KICAqIE9rZVYyX0xHMjlfMjU0NTU4MzMNCiAgKiBPa2VWMl9MRzI5XzI1NDgzNTk1DQogICogT2tlVjJfTEcyOV8yNTUxNTE2MQ0KICAqIE9rZVYyX0xHMzVfMjgwNzg4NjcNCiAgKiBPa2VWMl9MRzM1XzI4MTI4Njg3DQogICogT2tlVjJfTEczNV8yODE2NTA3Ng0KICAqIE9rZVYyX0xHMzVfMjgxNjcxNzINCg0KQWxsIEFIUlAgQ2h1bSBzYW1wbGVzIGZyb20gRmlzaCBDcmVlayAtIERvdWdsYXMgSXNsYW5kLCBQcm9zcGVjdCBDcmVlaywgQWRtaXJhbHR5IENyZWVrLCBhbmQgU2F3bWlsbCBDcmVlayB3ZXJlIGdlbm90eXBlZCBmb3IgdGhlIHBhcmVudGFnZSBwYW5lbCBhdCB0aGUgQURGJkcgR2VuZSBDb25zZXJ2YXRpb24gTGFib3JhdG9yeSAoR0NMKSBpbiBXaW50ZXIvU3ByaW5nIDIwMjUuIER1ZSB0byBwb29yIHRpc3N1ZSBxdWFsaXR5LCB0aGVyZSB3YXMgYSBoaWdoIHByb3BvcnRpb24gb2Ygc2FtcGxlcyB0aGF0IGZhaWxlZCB0byBnZW5vdHlwZSBhbmQvb3IgYXBwZWFyIHRvIGJlIGNvbnRhbWluYXRlZCAoaGlnaCBoZXRlcm96eWdvc2l0eSksIHJlc3VsdGluZyBpbiBsb3cgUUMgcG93ZXIgZm9yIHNldmVyYWwgcGxhdGVzLiBRQyBjcm9zc2NoZWNrcyBhcmUgY3VycmVudGx5IHVuZGVyd2F5IGF0IHRoZSBHQ0wgaW4gSnVseSAyMDI1LCBob3dldmVyLCBXZXMgd2FudHMgd2hhdGV2ZXIgZ2Vub3R5cGVzIHdlIGhhdmUgb24gaGFuZCBub3cgc28gd2lsbCBqdXN0IGZpbHRlciBvdXQgdGhvc2UgcGxhdGVzLg0KDQojIEltcG9ydCBEYXRhDQoNCiMjICpMb2N1c0NvbnRyb2wqDQoNCkNyZWF0ZSBgb2JqZWN0c2AsIGNyZWF0ZSAqTG9jdXNDb250cm9sKiBmb3IgYENodW1fQUhSUF8yNTdfdjEuMy4wLmANCmBgYHtyIExvY3VzQ29udHJvbCwgZXZhbD1GQUxTRX0NCmZzOjpkaXJfY3JlYXRlKCJvYmplY3RzIikNCkdDTHI6OmNyZWF0ZV9sb2N1c2NvbnRyb2wobWFya2Vyc3VpdGUgPSAiQ2h1bV9BSFJQXzI1N192MS4zLjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VybmFtZSA9IC51c2VybmFtZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzc3dvcmQgPSAucGFzc3dvcmQpDQpHQ0xyOjpzYXZlX29iamVjdHMob2JqZWN0cyA9ICJMb2N1c0NvbnRyb2wiLCBwYXRoID0gIm9iamVjdHMiKQ0KYGBgDQoNCkhybW0sIG9ubHkgMjUxIGxvY2kuIENvbmZpcm0gdGhhdCB0aGVzZSBhcmUgdGhlIHNhbWUgbG9jaSBydW4gaW4gdGhlIG1vc3QgcmVjZW50IEFIUlAgQ2h1bSBwcm9qZWN0IENNMDY5DQpgYGB7ciwgZXZhbD1GQUxTRX0NCnByb2JlX2xvY2lfQ00wNjkgPC0gcmVhZHI6OnJlYWRfdHN2KA0KICBmaWxlID0gIlY6L0xhYi9HZW5vdHlwaW5nL1NOUCBQcm9qZWN0cy9DaHVtL1Byb2plY3QgQ00wNjkgQUhSUCBQYXJlbnRhZ2UvR2Vub3R5cGUgRGF0YSBGaWxlcy9zZXQycnJfQ00wNjlfb3V0cHV0cy9wcm9iZXMudHh0IiwgDQogIHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JQ0KICBkcGx5cjo6ZGlzdGluY3QoYCNMb2N1c19JRGApICU+JQ0KICBkcGx5cjo6cHVsbCgpDQoNCmFsbC5lcXVhbChwcm9iZV9sb2NpX0NNMDY5LCBMb2N1c0NvbnRyb2wkbG9jdXNuYW1lcykNCmBgYA0KDQpZZXMsIGFsbCBhcmUgdGhlIHNhbWUuDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmxvY2kyNTEgPC0gTG9jdXNDb250cm9sJGxvY3VzbmFtZXMNCkdDTHI6OnNhdmVfb2JqZWN0cyhvYmplY3RzID0gImxvY2kyNTEiLCBwYXRoID0gIm9iamVjdHMiKQ0KYGBgDQoNCiMjIEdlbm90eXBlcw0KDQpSZWFkIGluIGdlbm90eXBlcyBieSBwcm9qZWN0IHRvIGdldCBgc2lsbHl2ZWNgLg0KYGBge3IsIGV2YWw9RkFMU0V9DQpybShMb2N1c0NvbnRyb2wpDQojIEdDTHI6Omxva2kycl9wcm9qKHByb2plY3RfbmFtZSA9IHBhc3RlMCgiQ00wIiwgNjQ6NzApLCB1c2VybmFtZSA9IC51c2VybmFtZSwgcGFzc3dvcmQgPSAucGFzc3dvcmQpICAjIGNvbW1lbnQgb3V0IHRvIGF2b2lkIHJlLXJ1bm5pbmcNCmBgYA0KDQpDb25maXJtIGNvbGxlY3Rpb25zLg0KYGBge3IsIGV2YWw9RkFMU0V9DQooc2lsbHl2ZWMgPC0gc29ydChwcm9qZWN0X3NpbGx5cykpDQpgYGANCg0KU3Vic2V0IGBzaWxseXZlY2AgdG8gcmVtb3ZlIGFsZXZpbiBjb2xsZWN0aW9uLg0KYGBge3IsIGV2YWw9RkFMU0V9DQooc2lsbHl2ZWMgPC0gZ3JlcChwYXR0ZXJuID0gImEiLCB4ID0gc2lsbHl2ZWMsIGlnbm9yZS5jYXNlID0gRkFMU0UsIHZhbHVlID0gVFJVRSwgaW52ZXJ0ID0gVFJVRSkpDQpgYGANCg0KV2lwZSBvdXQgYC5nY2xgIG9iamVjdHMgYW5kIHJlLXJlYWQgaW4gdXNpbmcgYGxva2kycmAuDQpgYGB7ciwgZXZhbD1GQUxTRX0NCkdDTHI6OnNhdmVfb2JqZWN0cyhvYmplY3RzID0gInNpbGx5dmVjIiwgcGF0aCA9ICJvYmplY3RzIikNCg0Kcm0obGlzdCA9IG9iamVjdHMocGF0dGVybiA9ICIuZ2NsIikpDQpybShMb2N1c0NvbnRyb2wpDQpgYGANCg0KUmVhZCBpbiBnZW5vdHlwZXMuDQpgYGB7ciBldmFsPUZBTFNFfQ0KR0NMcjo6bG9hZF9vYmplY3RzKHBhdGggPSAib2JqZWN0cyIsIHBhdHRlcm4gPSAiTG9jdXNDb250cm9sIikNCg0KR0NMcjo6bG9raTJyKHNpbGx5dmVjID0gc2lsbHl2ZWMsDQogICAgICAgICAgICAgdXNlcm5hbWUgPSAudXNlcm5hbWUsDQogICAgICAgICAgICAgcGFzc3dvcmQgPSAucGFzc3dvcmQsIA0KICAgICAgICAgICAgIHRlc3RfdHlwZSA9ICJHVFNOUCIpDQpgYGANCg0KT25seSB0b29rIDUuMzkgbWludXRlcyB0byByZWFkIGV2ZXJ5dGhpbmcgaW4gYXQgdGhlIG9mZmljZSENCg0KU2F2ZSByYXcgZ2Vub3R5cGVzICh1c2VmdWwgZm9yIHdvcmtpbmcgb2Zmc2l0ZSBhbmQgb2ZmIFZQTikuDQpgYGB7ciBldmFsPUZBTFNFfQ0KZnM6OmRpcl9jcmVhdGUoImRhdGEvZ2Vub3R5cGVzIikNCmZzOjpkaXJfY3JlYXRlKCJkYXRhL2dlbm90eXBlcy9yYXciKQ0KIyBHQ0xyOjpzYXZlX3NpbGx5cyhzaWxseXZlYyA9IHNpbGx5dmVjLCBwYXRoID0gImRhdGEvZ2Vub3R5cGVzL3JhdyIsIHJkcyA9IFRSVUUpICAjIGhpZGUgc28geW91IGRvbid0IG92ZXJ3cml0ZSBvbiBhY2NpZGVudCENCmBgYA0KDQojIyMgUmUtbG9hZA0KDQpSZS1sb2FkLCBpZiBuZWNlc3NhcnkNCmBgYHtyfQ0KR0NMcjo6bG9hZF9vYmplY3RzKHBhdGggPSAib2JqZWN0cyIpDQpHQ0xyOjpsb2FkX3NpbGx5cyhzaWxseXZlYyA9IHNpbGx5dmVjLCBwYXRoID0gImRhdGEvZ2Vub3R5cGVzL3JhdyIsIHJkcyA9IFRSVUUpDQpgYGANCg0KIyMgTWV0YWRhdGENCg0KUmVhZCBpbiBwYWlyZWQgbWV0YWRhdGEgZnJvbSBTYWxtb24gQmlvbG9naWNhbCBGYWN0IGFuZCBTdHJlYW0gU3BlY2ltZW5zLCBmb3JtYXQgc2ltaWxhciB0byBgcHdzX3BpbmtfcGVkX2RhdGAuDQoqKk5PVEUqKiBJJ20gZG9pbmcgdGhpcyBpbiBhIGJpdCBvZiBhIGh1cnJ5LCBzbyBtYWtlIHN1cmUgdG8gcHJvb2YgcmVhZCBjYXJlZnVsbHkgaWYgcmVjeWNsaW5nIHRoaXMgY29kZSENCmBgYHtyfQ0KKG1ldGFkYXRhIDwtIHJlYWRyOjpyZWFkX2NzdihmaWxlID0gIi4uL09jZWFuQUsvQUhSUCBTYWxtb24gQmlvbG9naWNhbCBEYXRhIDIwMjQwMTA1XzEyMjQwNC4zNjg0NTlfd2l0aF9zcGF3bmluZ19zdGF0ZS5jc3YiKSkNCmBgYA0KDQpNb2RpZnkgdG8gbWFrZSBzaW1pbGFyIHRvIGBwd3NfcGlua19wZWRfZGF0YCwgZGVzcGl0ZSBsYWNrIG9mIGxvY2F0aW9uIGRhdGEgZnJvbSBgcml2ZXJkaXN0YC4NCmBgYHtyfQ0KKA0KICBtZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUgDQogICAgZHBseXI6OnNlbGVjdCgtZHBseXI6OmNvbnRhaW5zKCJ0YXJnZXQiKSkgJT4lICAjIGRyb3AgR09EIGNvbHVtbnMNCiAgICBkcGx5cjo6c2VsZWN0KC1kcGx5cjo6Y29udGFpbnMoImRldGVybWluYXRpb24iKSkgJT4lICAjIGRyb3AgR09EIGNvbHVtbnMNCiAgICBkcGx5cjo6cmVuYW1lKGRhdGUgPSBzYW1wbGVfZGF0ZSwNCiAgICAgICAgICAgICAgICAgIGxlbmd0aCA9IGxlbmd0aF9tbSwNCiAgICAgICAgICAgICAgICAgIHNpbGx5ID0gc2lsbHlfY29kZSkgJT4lIA0KICAgIHRpZHlyOjp1bml0ZShjb2wgPSAic2FtcGxlIiwgZG5hX3RyYXlfY29kZTpkbmFfdHJheV93ZWxsX2NvZGUsIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpICU+JSAgIyBwcmltYXJ5IGRhdGEga2V5IGZvciBBSFJQLCBEV1AgYmFyY29kZSBfIHdlbGwgY29kZQ0KICAgIHRpZHlyOjp1bml0ZShjb2wgPSAic2lsbHlfc291cmNlIiwgYyhzaWxseSwgZmlzaF9pZCksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpICU+JSAgIyBwcmltYXJ5IGRhdGEga2V5IGZvciBHQ0wgY29sbGVjdGlvbnMNCiAgICBkcGx5cjo6bXV0YXRlKGZyYW56X2lkID0gc3RyaW5ncjo6c3RyX2Moc3RyaW5ncjo6c3RyX3N1YihzaWxseSwgMSwgMiksICAjIDFzdCBsZXR0ZXIgb2Ygc3BlY2llcyBuYW1lLCAxc3QgbGV0dGVyIG9mIHN0cmVhbSBuYW1lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3I6OnN0cl9zdWIoc2lsbHksIC0yLCAtMSksICAjIDIgZGlnaXQgc2FtcGxlIHllYXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdyOjpzdHJfcGFkKGZpc2hfaWQsIHdpZHRoID0gNSwgcGFkID0gIjAiLCBzaWRlID0gImxlZnQiKSksICAjIDUgZGlnaXQgZmlzaGlkIHVzaW5nIHBhZGRlZCB6ZXJvcyB1cCBmcm9udA0KICAgICAgICAgICAgICAgICAgeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihkYXRlKSwNCiAgICAgICAgICAgICAgICAgIHN0cmVhbSA9IHN0cmluZ3I6OnN0cl9yZW1vdmUoc3RyaW5nID0gbG9jYXRpb25fY29kZSwgcGF0dGVybiA9ICIgQ3JlZWsiKSwNCiAgICAgICAgICAgICAgICAgIERPWSA9IGx1YnJpZGF0ZTo6eWRheShkYXRlKSwgICMgZGF5IG9mIHllYXI7IERPWQ0KICAgICAgICAgICAgICAgICAgc2V4ID0gZHBseXI6OmNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAgICAgc2V4ID09ICJNIiB+ICJNYWxlIiwNCiAgICAgICAgICAgICAgICAgICAgc2V4ID09ICJGIiB+ICJGZW1hbGUiLA0KICAgICAgICAgICAgICAgICAgICBzZXggPT0gIlUiIH4gTkFfY2hhcmFjdGVyXywNCiAgICAgICAgICAgICAgICAgICAgaXMubmEoc2V4KSB+IE5BX2NoYXJhY3Rlcl8pLA0KICAgICAgICAgICAgICAgICAgb3JpZ2luID0gZHBseXI6OmNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAgICAgb3RvbGl0aF9tYXJrX3ByZXNlbnQgPT0gIk5PIiB+ICJOYXR1cmFsIiwNCiAgICAgICAgICAgICAgICAgICAgb3RvbGl0aF9tYXJrX3ByZXNlbnQgPT0gIllFUyIgfiAiSGF0Y2hlcnkiDQogICAgICAgICAgICAgICAgICApLCAgIyBhZGQgb3JpZ2luIHZhcmlhYmxlDQogICAgICAgICAgICAgICAgICBvcmlnaW4gPSBiYXNlOjpmYWN0b3Iob3JpZ2luLCBsZXZlbHMgPSBjKCJOYXR1cmFsIiwgIkhhdGNoZXJ5IikpLCAgIyBtYWtlIGZhY3RvciB0byBlbnN1cmUgaGF0Y2hlcnkgIT0gcmVkDQogICAgICAgICAgICAgICAgICBzcGF3bmluZ19zdGF0ZSA9IGRwbHlyOjpjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgICAgIHNwYXduaW5nX3N0YXRlID09ICJVbmtub3duIiB+IE5BX2NoYXJhY3Rlcl8sDQogICAgICAgICAgICAgICAgICAgIFRSVUUgfiBzcGF3bmluZ19zdGF0ZQ0KICAgICAgICAgICAgICAgICAgKSAgIyBmb3JjZSBVbmtub3duIHRvIE5BDQogICAgKSAlPiUgIA0KICAgIGRwbHlyOjpzZWxlY3QoDQogICAgICBmcmFuel9pZCwgICMgdW5pcXVlIGlkZW50aWZpZXIgZm9yIEZSQU56IChlLmcuIFBFMTZfMDAwMDEsIHNwZWNpZXMsIHN0cmVhbSwgeWVhciwgR0NMIGZpc2hfaWQpDQogICAgICBzYW1wbGUsICAjIHVsdGltYXRlIGRhdGEga2V5IGZvciBBSFJQLCBEV1AgYmFyY29kZSArIERXUCBwb3NpdGlvbg0KICAgICAgc2lsbHlfc291cmNlLCAgIyB1bmlxdWUgaWRlbnRpZmllciBmb3IgTE9LSSAoR0NMIGRhdGFiYXNlKQ0KICAgICAgc3RyZWFtLCAgIyA1IHBlZGlncmVlIHN0cmVhbXMNCiAgICAgIHllYXIsICAjIHNhbXBsZSB5ZWFyIChha2EgZGVhdGggeWVhciBmb3IgRlJBTnopDQogICAgICBvcmlnaW4sICAjIGhhdGNoZXJ5IG9yIHdpbGQNCiAgICAgICMgaGF0Y2hlcnksICAjIHdoaWNoIGhhdGNoZXJ5DQogICAgICBzZXgsICAjIG1hbGUsIGZlbWFsZSwgdW5rbm93bg0KICAgICAgZGF0ZSwgICMgc2FtcGxlIGRhdGUNCiAgICAgIERPWSwgICMgZGF5IG9mIHllYXIgKGFrYSBqdWxpYW4gc2FtcGxlIGRhdGUpDQogICAgICBsZW5ndGgsICAjIG1pZGV5ZSB0byBoeXB1cmFsIHBsYXRlIGxlbmd0aCBpbiBtbQ0KICAgICAgIyBpbnRlcnRpZGFsLCAgIyBzYW1wbGluZyBsb2NhdGlvbiBhYm92ZSBvciBiZWxvdyBpbnRlcnRpZGFsDQogICAgICAjIGRpc3RhbmNlX21vdXRoLCAgIyByaXZlcmRpc3QgYXBwcm94aW1hdGUgc2FtcGxpbmcgbG9jYXRpb24gZnJvbSBtb3V0aCBpbiBtZXRlcnMgDQogICAgICBzcGF3bmluZ19zdGF0ZSwgICMgYWxpdmUsIHBpbmsgZ2lsbCwgZ3JleSBnaWxsLCByb3R0aW5nIChha2EgaW5kZXggb2Ygc3RyZWFtIGxpZmUpDQogICAgICAjIHByZV9zcGF3biwgICMgVFJVRS9GQUxTRSAoYWRkZWQgaW4gMjAxNik7IGlmIHRoZSBlZ2dzIGFuZCBtaWx0IGRvIG5vdCBlYXNpbHkgcmVsZWFzZSBhbmQgY2FyY2FzcyBoYXMgY29tcGxldGVseSBmdWxsIGdvbmFkcw0KICAgICAgIyBwYXJ0aWFsX3NwYXduLCAgIyBUUlVFL0ZBTFNFIChhZGRlZCBpbiAyMDE1KTsgZmlzaCBpcyBzYW1wbGVkIChkZWFkIG9yIGFsaXZlKSB3aXRoIG1vcmUgdGhhbiBhIGxpdHRsZSBlZ2dzIG9yIG1pbHQNCiAgICAgICMgcHJleWVkX3Vwb24sICAjIFRSVUUvRkFMU0UgKGFkZGVkIGluIDIwMTUpOyBnb25hZHMgcmVtb3ZlZCBieSBwcmVkYXRvcnMNCiAgICAgICMgc3RyZWFtX3RyaWIsICAjIHN0cmVhbSB0cmlidXRhcnkgKEdpbG1vdXIsIFBhZGR5LCBhbmQgU3RvY2tkYWxlIGhhdmUgbXVsdGlwbGUpDQogICAgICAjIGxhdGl0dWRlLCAgIyBzYW1wbGluZyBhcmVhICh3aGVyZSBmaXNoIHdlcmUgbW92ZWQgdG8gcHJpb3IgdG8gc2FtcGxpbmcsIHJvdWdoIGFwcHJveGltYXRlIG9mIHNwYXduaW5nIGxvY2F0aW9uKQ0KICAgICAgIyBsb25naXR1ZGUsICAjIHNhbXBsaW5nIGFyZWEgKHdoZXJlIGZpc2ggd2VyZSBtb3ZlZCB0byBwcmlvciB0byBzYW1wbGluZywgcm91Z2ggYXBwcm94aW1hdGUgb2Ygc3Bhd25pbmcgbG9jYXRpb24pDQogICAgICBvdG9saXRoX21hcmtfcHJlc2VudCwgICMgb3RvbGl0aCBtYXJrIHByZXNlbnQgKHllcyA9IGhhdGNoZXJ5LCBubyA9IG5hdHVyYWwpDQogICAgICBvdG9saXRoX21hcmtfaWQsICAjIG90b2xpdGggaGF0Y2hlcnkgbWFyaw0KICAgICAgb3RvbGl0aF9tYXJrX3N0YXR1c19jb2RlLCAgIyBvdG9saXRoIHJlYWQgc3RhdHVzIGNvZGUgKHJlYXNvbiBmb3Igbm8gcmVhZCA9IG92ZXJncm91bmQsIGxvc3QsIGV0Yy4pDQogICAgICBzaWxseSwgICMgR0NMIGNvbGxlY3Rpb24gY29kZQ0KICAgICAgZmlzaF9pZCwgICMgR0NMIGZpc2ggSUQgd2l0aGluIGEgc2lsbHkNCiAgICAgIGRuYV90cmF5X2NvZGUsICAjIDEwLWRpZ2l0IERXUCBiYXJjb2RlDQogICAgICBkbmFfdHJheV93ZWxsX2NvZGUsICAjIDQ4IERXUCBwb3NpdGlvbiAxOjQ4DQogICAgICAjIGRuYV90cmF5X3dlbGxfcG9zLCAgIyA0OCBEV1AgcG9zaXRpb24gQTE6SDYNCiAgICAgICMgZGlzdGFuY2VfdGlkZSwgICMgcml2ZXJkaXN0IGFwcHJveGltYXRlIHNhbXBsaW5nIGxvY2F0aW9uIGZyb20gbWF4aW11bSBoaWdoIHRpZGUNCiAgICAgICMgcml2ZXJkaXN0X3NlZywgICMgcml2ZXJkaXN0IHJpdmVyIHNlZ21lbnQNCiAgICAgICMgcml2ZXJkaXN0X3ZlcnQsICAjIHJpdmVyZGlzdCB2ZXJ0ZXgNCiAgICAgICMgcml2ZXJkaXN0X3NuYXBkaXN0LCAgIyByaXZlcmRpc3Qgc25hcHBpbmcgZGlzdGFuY2UgKGFrYSBob3cgZmFyIHdhcyBzYW1wbGluZyBsb2NhdGlvbiBsYXQvbG9uZyBmcm9tIHRoZSBzdHJlYW0gcG9seWxpbmUpDQogICAgICAjIGhpZ2hfdGlkZSAgIyBoaWdoZXN0IGV4dGVudCBvZiBoaWdoIHRpZGUgaW4gbWV0ZXJzIGZyb20gbW91dGgsIHVuaXF1ZSB0byBlYWNoIHN0cmVhbQ0KICAgICAgZndfYWdlLA0KICAgICAgc3dfYWdlDQogICAgKSAgDQopICAgICAgIA0KYGBgDQoNCiMgU2FtcGxlIFNpemVzDQoNClZlcmlmeSB0aGF0IGFsbCBnZW5vdHlwZWQgc2FtcGxlcyBoYXZlIG1ldGFkYXRhLg0KYGBge3J9DQpnZW5vX3NpbGx5X3NvdXJjZSA8LSBzYXBwbHkoc2lsbHl2ZWMsIGZ1bmN0aW9uKHNpbGx5KSB7Z2V0KHBhc3RlMChzaWxseSwgIi5nY2wiKSkkU2lsbHlTb3VyY2V9KSAlPiUgdW5saXN0KCkNCg0KdGFibGUoZ2Vub19zaWxseV9zb3VyY2UgJWluJSBtZXRhZGF0YSRzaWxseV9zb3VyY2UpDQpgYGANCg0KU2hvb3QsIHdoaWNoIHNhbXBsZXMgZG8gbm90IGhhdmUgYW55IG1ldGFkYXRhPw0KDQpBaCwgSSBiZXQgdGhleSBhcmUgdGhlIEZpc2ggQ3JlZWsgdGFnIHNhbXBsZXMgZnJvbSAyMDEzIHRoYXQgVHlsZXIgRGFubiBhbmQgSGVhdGhlciBMaWxsZXIgY29sbGVjdGVkIHByZS1BSFJQIGZpZWxkIGNvbGxlY3Rpb25zIQ0KYGBge3J9DQpzZXRkaWZmKGdlbm9fc2lsbHlfc291cmNlLCBtZXRhZGF0YSRzaWxseV9zb3VyY2UpDQpgYGANCg0KQ29ycmVjdC4gQWRkIHRoZXNlIHRvIG1ldGFkYXRhIHdpdGggc2FtcGxlIGRhdGUgaW5mby4NCmBgYHtyfQ0KbWV0YWRhdGEgPC0gDQogIGRwbHlyOjpiaW5kX3Jvd3MobWV0YWRhdGEsDQogICAgICAgICAgICAgICAgICAgQ01GSVNIQ1JUMTMuZ2NsICU+JSANCiAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoU0lMTFlfQ09ERSwgRktfRklTSF9JRCwgU2lsbHlTb3VyY2UsIEROQV9UUkFZX0NPREUsIEROQV9UUkFZX1dFTExfQ09ERSwgQ0FQVFVSRV9EQVRFKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHNpbGx5ID0gU0lMTFlfQ09ERSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlzaF9pZCA9IEZLX0ZJU0hfSUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpbGx5X3NvdXJjZSA9IFNpbGx5U291cmNlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG5hX3RyYXlfY29kZSA9IEROQV9UUkFZX0NPREUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRuYV90cmF5X3dlbGxfY29kZSA9IEROQV9UUkFZX1dFTExfQ09ERSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZSA9IENBUFRVUkVfREFURSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgdGlkeXI6OnVuaXRlKGNvbCA9ICJzYW1wbGUiLCBkbmFfdHJheV9jb2RlOmRuYV90cmF5X3dlbGxfY29kZSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgdGlkeXI6OnVuaXRlKGNvbCA9ICJzaWxseV9zb3VyY2UiLCBjKHNpbGx5LCBmaXNoX2lkKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgZHBseXI6Om11dGF0ZShmcmFuel9pZCA9IHN0cmluZ3I6OnN0cl9jKHN0cmluZ3I6OnN0cl9zdWIoc2lsbHksIDEsIDIpLCAgIyAxc3QgbGV0dGVyIG9mIHNwZWNpZXMgbmFtZSwgMXN0IGxldHRlciBvZiBzdHJlYW0gbmFtZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3I6OnN0cl9zdWIoc2lsbHksIC0yLCAtMSksICAjIDIgZGlnaXQgc2FtcGxlIHllYXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5ncjo6c3RyX3BhZChmaXNoX2lkLCB3aWR0aCA9IDUsIHBhZCA9ICIwIiwgc2lkZSA9ICJsZWZ0IikpLCAgIyA1IGRpZ2l0IGZpc2hpZCB1c2luZyBwYWRkZWQgemVyb3MgdXAgZnJvbnQpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGUgPSBsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSBsdWJyaWRhdGU6OnllYXIoZGF0ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmVhbSA9ICJGaXNoIC0gRG91Z2xhcyBJc2xhbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBET1kgPSBsdWJyaWRhdGU6OnlkYXkoZGF0ZSkgICMgZGF5IG9mIHllYXINCiAgICAgICAgICAgICAgICAgICAgICkgDQopDQpgYGANCg0KVmVyaWZ5IGFsbCBzYW1wbGVzIGhhdmUgbWV0YWRhdGEuDQpgYGB7cn0NCnRhYmxlKGdlbm9fc2lsbHlfc291cmNlICVpbiUgbWV0YWRhdGEkc2lsbHlfc291cmNlKQ0KYGBgDQoNCkV4Y2VsbGVudCwgYWxsIHByZXNlbnQgYW5kIGFjY291bnRlZCBmb3IuDQoNClJlY29yZCB3aGljaCBzYW1wbGVzIHdlcmUgZ2Vub3R5cGVkLg0KYGBge3J9DQptZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUgDQogIGRwbHlyOjptdXRhdGUoZ2Vub3R5cGVkID0gc2lsbHlfc291cmNlICVpbiUgZ2Vub19zaWxseV9zb3VyY2UpDQpgYGANCg0KSG93IG1hbnkgc2FtcGxlcyBnZW5vdHlwZWQgcGVyIGNvbGxlY3Rpb24gKHNpbGx5KT8NCmBgYHtyfQ0KbWV0YWRhdGEgJT4lIA0KICBkcGx5cjo6Y291bnQoZ2Vub3R5cGVkLCBzaWxseSkgJT4lIA0KICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGdlbm90eXBlZCwgdmFsdWVzX2Zyb20gPSBuKQ0KYGBgDQoNCiMjIENoZWNrIEZvciBTYW1wbGUgRHVwbGljYXRlcw0KDQpJIGdvdCBhIHdlaXJkIG1hbnktdG8tbWFueSBlcnJvciBpbiBhIGpvaW4gdGhlIGZpcnN0IHRpbWUgSSByYW4gdGhyb3VnaCB0aGlzIHNjcmlwdCwgc28gSSB3YW50IHRvIGNoZWNrIGFuZCBzZWUgaWYgdGhlcmUgYXJlIGFjdHVhbGx5IGR1cGxpY2F0ZSBzYW1wbGVzIHByZXNlbnQgYW5kIGlmIHNvLCByZW1vdmUgdGhlbSENCmBgYHtyfQ0KIyBiaW5kIGFsbCBzYW1wbGVzIHRvZ2V0aGVyIGludG8gYSBzaW5nbGUgLmdjbCBvYmplY3QNCm15LmdjbCA8LSBzYXBwbHkoc2lsbHl2ZWMsIGZ1bmN0aW9uKHNpbGx5KSB7DQogIG15LnNpbGx5IDwtIGdldChwYXN0ZTAoc2lsbHksICIuZ2NsIikpDQp9LCBzaW1wbGlmeSA9IEZBTFNFKSAlPiUgDQogIGRwbHlyOjpiaW5kX3Jvd3MoKQ0KYGBgDQoNCkFueSBkdXBsaWNhdGUgYFNpbGx5U291cmNlYD8NCmBgYHtyfQ0KdGFibGUodGFibGUobXkuZ2NsJFNpbGx5U291cmNlKSkNCmBgYA0KDQpOb3BlLCBhbGwgZ29vZCB0aGVyZS4gQ2xlYW4gdXAuDQpgYGB7cn0NCnJtKG15LmdjbCkNCmBgYA0KDQpBcmUgdGhlcmUgbWV0YWRhdGEgZHVwbGljYXRlcz8NCmBgYHtyfQ0KdGFibGUodGFibGUobWV0YWRhdGEkc2lsbHlfc291cmNlKSkNCmBgYA0KDQpXZWxsIHNvbiBvZiBhIGJpc2N1aXQsIHRoZXJlIGFyZSENCmBgYHtyfQ0KbWV0YWRhdGEgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGR1cGxpY2F0ZWQoc2lsbHlfc291cmNlKSB8IGR1cGxpY2F0ZWQoc2lsbHlfc291cmNlLCBmcm9tTGFzdCA9IFRSVUUpKSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKHNpbGx5X3NvdXJjZSkNCmBgYA0KDQpPa2F5LCB0aGV5IGFwcGVhciB0byBiZSB0cnVlIGR1cGxpY2F0ZXMgaW4gYG1ldGFkYXRhYCwgbm8gY29uZmxpY3RpbmcgY29sdW1ucy4gUmVtb3ZlIGR1cGxpY2F0ZSByb3dzLg0KYGBge3J9DQptZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChzaWxseV9zb3VyY2UsIC5rZWVwX2FsbCA9IFRSVUUpDQpgYGANCg0KIyMgSm9pbiBNZXRhZGF0YQ0KDQpKb2luIGBtZXRhZGF0YWAgdG8gZWFjaCBzaWxseSBieSBgU2lsbHlTb3VyY2VgLg0KYGBge3J9DQp4IDwtIHNhcHBseShzaWxseXZlYywgZnVuY3Rpb24oc2lsbHkpIHsNCiAgbXkuc2lsbHkgPC0gZ2V0KHBhc3RlMChzaWxseSwgIi5nY2wiKSkNCiAgbXkuc2lsbHkgPC0gbXkuc2lsbHkgJT4lIGRwbHlyOjpsZWZ0X2pvaW4oDQogICAgeSA9IG1ldGFkYXRhICU+JSBkcGx5cjo6c2VsZWN0KC1nZW5vdHlwZWQpLA0KICAgIGJ5ID0gZHBseXI6OmpvaW5fYnkoU2lsbHlTb3VyY2UgPT0gc2lsbHlfc291cmNlKQ0KICApDQogIGFzc2lnbih4ID0gcGFzdGUwKHNpbGx5LCAiLmdjbCIpLA0KICAgICAgICAgdmFsdWUgPSBteS5zaWxseSwNCiAgICAgICAgIHBvcyA9IDEpDQp9KQ0Kcm0oeCkgICMgc3VwcHJlc3NlcyBvdXRwdXQNCmBgYA0KDQojIEZpbHRlciBRQyBDcm9zc2NoZWNrIFBsYXRlcw0KDQpDcmVhdGUgYSB2ZWN0b3Igb2YgcGxhdGUgSURzIHRvIHRvc3MsIHBlbmRpbmcgUUMgY3Jvc3NjaGVja3MuDQpgYGB7cn0NCnBsYXRlX2lkc19xY3hjaGVjayA8LSBjKDY0NDYxLCA2NDQ2MiwgNjQ0ODAsIDY0NDgyLCA2NDQ4MywgNjQ0ODQsIDY0NDg1LCA2NDQ4NiwgNjQ1MDIsIDY0NTAzLCA2NDUwOSwgNjQ1MTEsIDY0NTEyLCA2NDUxMywgNjQ1MTQsIDY0NTE1LCA2NDU0MiwgNjQ1NDMsIDY0NTgyLCA1NjM2MiwgNTYzNjMpICU+JSBhcy5jaGFyYWN0ZXIoKQ0KYGBgDQoNCkdldCBzYW1wbGUgc2l6ZSBieSBzaWxseS4NCmBgYHtyfQ0KKA0KICBzYW1wbGVfc2l6ZV9xY3hjaGVjayA8LSBkcGx5cjo6dGliYmxlKHNpbGx5ID0gc2lsbHl2ZWMpICU+JQ0KICAgIGRwbHlyOjptdXRhdGUoZ2Vub3R5cGVkID0gR0NMcjo6c2lsbHlfbihzaWxseXZlYyA9IHNpbGx5dmVjKSAlPiUgZHBseXI6OnB1bGwobikpDQopDQpgYGANCg0KUmVtb3ZlIGFueSBmaXNoIG9uIHRoZSBwbGF0ZXMgcGVuZGluZyBRQyBjcm9zc2NoZWNrcy4NCmBgYHtyfQ0KeCA8LSBzYXBwbHkoc2lsbHl2ZWMsIGZ1bmN0aW9uKHNpbGx5KSB7DQogIG15LnNpbGx5IDwtIGdldChwYXN0ZTAoc2lsbHksICIuZ2NsIikpDQogIG15LnNpbGx5IDwtIG15LnNpbGx5ICU+JSBkcGx5cjo6ZmlsdGVyKCFzdHJpbmdyOjpzdHJfZGV0ZWN0KHN0cmluZyA9IFBMQVRFX0lELCBwYXR0ZXJuID0gc3RyaW5ncjo6c3RyX2MocGxhdGVfaWRzX3FjeGNoZWNrLCBjb2xsYXBzZSA9ICJ8IikpKQ0KICBhc3NpZ24oeCA9IHBhc3RlMChzaWxseSwgIi5nY2wiKSwNCiAgICAgICAgIHZhbHVlID0gbXkuc2lsbHksDQogICAgICAgICBwb3MgPSAxKQ0KfSkNCnJtKHgpICAjIHN1cHByZXNzZXMgb3V0cHV0DQpgYGANCg0KSG93IG1hbnkgZmlzaCB3ZXJlIHJlbW92ZWQgYnkgc2lsbHk/DQpgYGB7cn0NCigNCiAgc2FtcGxlX3NpemVfcWN4Y2hlY2sgPC0gc2FtcGxlX3NpemVfcWN4Y2hlY2sgJT4lDQogICAgZHBseXI6Om11dGF0ZShxY3hjaGVjayA9IGdlbm90eXBlZCAtIEdDTHI6OnNpbGx5X24oc2lsbHl2ZWMgPSBzaWxseXZlYykgJT4lIGRwbHlyOjpwdWxsKG4pKQ0KKQ0KYGBgDQoNCkhvdyBtYW55IHRvdGFsIHNhbXBsZXM/DQpgYGB7cn0NCnN1bShzYW1wbGVfc2l6ZV9xY3hjaGVjayRxY3hjaGVjaykNCmBgYA0KDQpPa2F5LCB0aGF0IGxvb2tzIGFib3V0IHJpZ2h0IGZvciAyMSBwbGF0ZXMuIE11c3QgaGF2ZSBiZWVuIHNvbWUgcGFydGlhbCBwbGF0ZXMuDQoNCiMgR2Vub3R5cGUgUUENCg0KU3RhbmRhcmQgZGF0YSBRQSAoR1Qtc2VxLCBubyBgY29uU2NvcmVgKToNCg0KICAqIFJlbW92ZSBmaXNoIG1pc3NpbmcgZ2Vub3R5cGVzICgqKjw4MCUqKiBsb2NpKQ0KICAqIFJlbW92ZSBjb250YW1pbmF0aW9uIChoZXRlcm96eWdvc2l0eSBvdXRsaWVycyAqKisvLSAzLjUqKiBtb2RpZmllZCB6LXNjb3JlKQ0KICAqIFJlbW92ZSBkdXBsaWNhdGVzICgqKj45NSUqKiBsb2NpIGNvbmNvcmRhbmNlLCByZW1vdmUgKipib3RoKiogZHVwbGljYXRlcykNCg0KYGBge3J9DQooDQogIHNhbXBsZV9zaXplX3FhIDwtIGRwbHlyOjp0aWJibGUoc2lsbHkgPSBzaWxseXZlYykgJT4lDQogICAgZHBseXI6Om11dGF0ZShnZW5vdHlwZWQgPSBHQ0xyOjpzaWxseV9uKHNpbGx5dmVjID0gc2lsbHl2ZWMpICU+JSBkcGx5cjo6cHVsbChuKSkNCikNCmBgYA0KDQojIyBNaXNzaW5nIExvY2kgKDw4MCUpDQoNClJlbW92ZSBmaXNoIHdpdGggPDgwJSBsb2NpIGdlbm90eXBlZC4NCmBgYHtyfQ0KbWlzc19sb2NpIDwtDQogIEdDTHI6OnJlbW92ZV9pbmRfbWlzc19sb2NpKHNpbGx5dmVjID0gc2lsbHl2ZWMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3BvcnRpb24gPSAwLjgpDQoNCiMgbWlzc19sb2NpJElEc19SZW1vdmVkDQoNCkdDTHI6OnNhdmVfb2JqZWN0cyhvYmplY3RzID0gIm1pc3NfbG9jaSIsIHBhdGggPSAgIi4uL29iamVjdHMiLCByZHMgPSBUUlVFKQ0KDQooDQogIHNhbXBsZV9zaXplX3FhIDwtIHNhbXBsZV9zaXplX3FhICU+JQ0KICAgIGRwbHlyOjptdXRhdGUobWlzc2luZyA9IGdlbm90eXBlZCAtIEdDTHI6OnNpbGx5X24oc2lsbHl2ZWMgPSBzaWxseXZlYykgJT4lIGRwbHlyOjpwdWxsKG4pKQ0KKQ0KYGBgDQoNCk92ZXJhbGwgYHIgZm9ybWF0KHN1bShzYW1wbGVfc2l6ZV9xYSRtaXNzaW5nKSwgYmlnLm1hcmsgPSAiLCIpYCBzYW1wbGVzIGRyb3BwZWQgZHVlIHRvIHBvb3IgcXVhbGl0eSBETkEuIA0KDQpXaGF0IHdhcyBnZW5vdHlwaW5nIHN1Y2Nlc3MgYnkgc3Bhd25pbmcgc3RhdGU/DQpgYGB7cn0NCm1pc3NfbG9jaV90aWIgPC0gdGliYmxlOjplbmZyYW1lKG1pc3NfbG9jaSRJRHNfUmVtb3ZlZCwgbmFtZSA9ICJzaWxseSIsIHZhbHVlID0gImZpc2hfaWQiKSAlPiUgDQogIHRpZHlyOjp1bm5lc3RfbG9uZ2VyKGZpc2hfaWQpICU+JSANCiAgZHBseXI6OmxlZnRfam9pbih5ID0gbWV0YWRhdGEsIGJ5ID0gZHBseXI6OmpvaW5fYnkoc2lsbHksIGZpc2hfaWQpKQ0KDQptZXRhZGF0YSAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoZ2Vub3R5cGVkKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoZHJvcF9taXNzID0gc2lsbHlfc291cmNlICVpbiUgbWlzc19sb2NpX3RpYiRzaWxseV9zb3VyY2UpICU+JSANCiAgZHBseXI6OmNvdW50KGRyb3BfbWlzcywgc3Bhd25pbmdfc3RhdGUpICU+JSANCiAgdGlkeXI6OnBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBkcm9wX21pc3MsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICBkcGx5cjo6cmVuYW1lKG5fZHJvcCA9IGBUUlVFYCwgbl9rZWVwID0gYEZBTFNFYCkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKG4gPSBuX2tlZXAgKyBuX2Ryb3AsDQogICAgICAgICAgICAgICAgcF9rZWVwID0gbl9rZWVwIC8gbiwNCiAgICAgICAgICAgICAgICBzcGF3bmluZ19zdGF0ZSA9IGZhY3RvcihzcGF3bmluZ19zdGF0ZSwgbGV2ZWxzID0gYygiQWxpdmUiLCAiUGluayBHaWxsIiwgIkdyZXkgR2lsbCIsICJSb3R0aW5nIikpKSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKHNwYXduaW5nX3N0YXRlKQ0KYGBgDQoNCkFzIGV4cGVjdGVkLCBnZW5vdHlwaW5nIHN1Y2Nlc3Mgd2FzIGxvd2VyIGZvciByb3R0aW5nIGZpc2gsIGJ1dCBob25lc3RseSwgbm90IHRlcnJpYmxlIQ0KDQojIyBDb250YW1pbmF0aW9uIChIZXRlcm96eWdvc2l0eSA+ICsvLSAzLjUgbW9kaWZpZWQgWi1zY29yZSkNCg0KUmVtb3ZlIGNvbnRhbWluYXRlZCBzYW1wbGVzIHByaW9yIHRvIGNoZWNraW5nIGZvciBkdXBsaWNhdGVzIQ0KDQpJbmRpdmlkdWFsIGhldGVyb3p5Z29zaXR5IG91dGxpZXJzIGlzIG91ciBiZXN0IG1ldHJpYyBmb3IgcmVtb3ZpbmcgY29udGFtaW5hdGVkIHNhbXBsZXMgaW4gdGhlIGFic2VuY2Ugb2Ygc29tZXRoaW5nIG1vcmUgc29waGlzdGljYXRlZCBsaWtlICpjb25TY29yZSogZnJvbSBbR1RzY29yZV0oaHR0cHM6Ly9naXRodWIuY29tL2dqbWNraW5uZXkvR1RzY29yZT90YWI9cmVhZG1lLW92LWZpbGUjc2FtcGxlLXN1bW1hcmllcykuIEluIHByZXZpb3VzIGFuYWx5c2VzLCBpbmNsdWRpbmcgW1NoZWRkIGV0IGFsLiAyMDIyXShodHRwczovL2RvaS5vcmcvMTAuMTExMS9ldmEuMTMzNTYpLCB3ZSd2ZSB1c2VkIHRoZSBzdGFuZGFyZCAxLjUgSVFSIG91dGxpZXIgZGV0ZWN0aW9uIG1ldGhvZCB0byByZW1vdmUgZXhjZXNzaXZlbHkgaGV0ZXJvenlnb3VzIGluZGl2aWR1YWxzLiBIb3dldmVyLCB0aGUgMS41IElRUiBtZXRob2QgYXNzdW1lcyBhIG5vcm1hbCBkaXN0cmlidXRpb24sIGJ1dCBoZWFydCBzYW1wbGUgaGV0ZXJvenlnb3NpdGllcyB0ZW5kIHRvIGJlIHNrZXdlZCByaWdodCBkdWUgdG8gY29udGFtaW5hdGlvbi4gQXMgb2YgMjAyNC0wMi0wOCwgd2UgZGVjaWRlZCB0byBwaXZvdCB0byB1c2luZyBbbW9kaWZpZWQgWi1zY29yZXNdKGh0dHBzOi8vd3d3Lml0bC5uaXN0Lmdvdi9kaXY4OTgvaGFuZGJvb2svZWRhL3NlY3Rpb24zL2VkYTM1aC5odG0pIHdpdGggY3V0b2ZmcyBvZiArLy0gMy41IGFzIHJlY29tbWVuZGVkIGJ5IElnbGV3aWN6IGFuZCBIb2FnbGluIFteMV0uIFRoZSBtb2RpZmllZCBaLXNjb3JlIHVzZXMgdGhlIG1lZGlhbiBhbmQgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAoTUFEKSBpbnN0ZWFkIG9mIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBpcyB0aHVzIG1vcmUgcm9idXN0IHRvIG91dGxpZXJzIGFuZCBhc3ltbWV0cmljYWwgZGlzdHJpYnV0aW9ucy4NCg0KW14xXTogQm9yaXMgSWdsZXdpY3ogYW5kIERhdmlkIEhvYWdsaW4gKDE5OTMpLCAiVm9sdW1lIDE2OiBIb3cgdG8gRGV0ZWN0IGFuZCBIYW5kbGUgT3V0bGllcnMiLCBUaGUgQVNRQyBCYXNpYyBSZWZlcmVuY2VzIGluIFF1YWxpdHkgQ29udHJvbDogU3RhdGlzdGljYWwgVGVjaG5pcXVlcywgRWR3YXJkIEYuIE15a3l0a2EsIFBoLkQuLCBFZGl0b3IuIA0KDQpTb3VyY2UgZnVuY3Rpb24gYW5kIGNhbGN1bGF0ZSBpbmRpdmlkdWFsIGhldGVyb3p5Z29zaXRpZXMgZnJvbSBgQUhSUC1QV1MtUGluay1TYWxtb24tUGVkaWdyZWVzYCBHaXRIdWIgcmVwby4NCmBgYHtyfQ0Kc291cmNlKCJ+L0dpdEh1Yl9yZXBvcy9BSFJQLVBXUy1QaW5rLVNhbG1vbi1QZWRpZ3JlZXMvY29kZS9mdW5jdGlvbnMvY2FsY19pbmRfaGV0LlIiKQ0KDQooDQogIGluZF9oZXQgPC0NCiAgICBjYWxjX2luZF9oZXQoDQogICAgICBzaWxseXZlYyA9IHNpbGx5dmVjLA0KICAgICAgbG9jaSA9IGxvY2kyNTEsDQogICAgICBuY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAtIDQNCiAgICApDQopDQpgYGANCg0KQ2FsY3VsYXRlICsvLSAzLjUgbW9kaWZpZWQgei1zY29yZSBjdXRvZmZzIGJ5ICpzdHJlYW0qLiANCioqTk9URSoqIHRoaXMgbWVhbnMgdGhhdCB0aGUgZXhhY3QgaGV0ZXJvenlnb3NpdHkgY3V0b2ZmIHdpbGwgdmFyeSBzbGlnaHRseSBieSBzdHJlYW0uIEZvciBQV1MgUGluayBTYWxtb24sIEkgZGlkIGl0IHNlcGFyYXRlbHkgYnkgc2lsbHksIGJ1dCBzb21lIG9mIG91ciBzYW1wbGUgc2l6ZXMgYXJlIHByZXR0eSBzbWFsbCBoZXJlIGFuZCB3ZSBoYXZlIHRoZSBzZXBhcmF0ZSAidGFnIiBzaWxseXMuIEknbSBvcHRpbmcgdG8gZG8gdGhpcyBieSBzdHJlYW0gKGFsbCB5ZWFycyB0b2dldGhlcikgdG8gYWxsb3cgZm9yIHN1YnRsZSBjaGFuZ2VzIGluIGFsbGVsZSBmcmVxdWVuY3kgcGVyIHN0cmVhbS4NCmBgYHtyfQ0KIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAoTUFEKQ0KbWFkIDwtIGZ1bmN0aW9uKHgpIHsNCiAgbWVkaWFuKGFicyh4IC0gbWVkaWFuKHgpKSkNCn0NCg0KIyBDYWxjdWxhdGUgdGhlIG1vZGlmaWVkIHotc2NvcmUgZm9yIGVhY2ggaW5kaXZpZHVhbA0KKA0KICBpbmRfaGV0IDwtIGluZF9oZXQgJT4lDQogICAgZHBseXI6Om11dGF0ZSgNCiAgICAgIHllYXIgPSAyMDAwICsgYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfc3ViKA0KICAgICAgICBzdHJpbmcgPSBzaWxseSwNCiAgICAgICAgc3RhcnQgPSAtMiwNCiAgICAgICAgZW5kID0gLTENCiAgICAgICkpLA0KICAgICAgc3RyZWFtID0gZHBseXI6OmNhc2Vfd2hlbigNCiAgICAgICAgc3RyaW5ncjo6c3RyX2RldGVjdChzdHJpbmcgPSBzaWxseSwgcGF0dGVybiA9ICJBRE0iKSB+ICJBZG1pcmFsdHkiLA0KICAgICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KHN0cmluZyA9IHNpbGx5LCBwYXR0ZXJuID0gIkZJU0giKSB+ICJGaXNoIiwNCiAgICAgICAgc3RyaW5ncjo6c3RyX2RldGVjdChzdHJpbmcgPSBzaWxseSwgcGF0dGVybiA9ICJQUk9TIikgfiAiUHJvc3BlY3QiLA0KICAgICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KHN0cmluZyA9IHNpbGx5LCBwYXR0ZXJuID0gIlNBVyIpIH4gIlNhd21pbGwiLA0KICAgICAgICBUUlVFIH4gIm1pc3Rha2VzX3dlcmVfbWFkZSINCiAgICAgICkNCiAgICApICU+JQ0KICAgIGRwbHlyOjpncm91cF9ieShzdHJlYW0pICU+JSAgIyBjdXRvZmZzIGFyZSBzcGVjaWZpYyB0byBlYWNoIHN0cmVhbSENCiAgICBkcGx5cjo6bXV0YXRlKA0KICAgICAgbW9kaWZpZWRfel9zY29yZSA9IDAuNjc0NSAqIChoZXQgLSBtZWRpYW4oaGV0KSkgLyBtYWQoaGV0KSwNCiAgICAgIG91dGxpZXIgPSBkcGx5cjo6Y2FzZV93aGVuKGFicyhtb2RpZmllZF96X3Njb3JlKSA+IDMuNSB+IFRSVUUsIFRSVUUgfiBGQUxTRSkNCiAgICApICU+JQ0KICAgIGRwbHlyOjp1bmdyb3VwKCkNCikNCiAgDQpHQ0xyOjpzYXZlX29iamVjdHMoImluZF9oZXQiLCBwYXRoID0gIm9iamVjdHMiLCByZHMgPSBUUlVFKQ0KYGBgDQoNClBsb3QgZGlzdHJpYnV0aW9uIG9mIGhldGVyb3p5Z29zaXR5IGFuZCBzaG93IG91dGxpZXJzLg0KYGBge3Igd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD0xMH0NCmluZF9oZXQgJT4lIA0KICBnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHggPSBoZXQsIGZpbGwgPSBvdXRsaWVyKSkgKw0KICBnZ3Bsb3QyOjpnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEgLyBsZW5ndGgobG9jaTI1MSkpICsNCiAgZ2dwbG90Mjo6ZmFjZXRfZ3JpZChyb3dzID0gZ2dwbG90Mjo6dmFycyhzdHJlYW0pLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICBnZ3Bsb3QyOjp4bGltKDAsIDEpICsNCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJPdXRsaWVyIiwgdmFsdWVzID0gYygiVFJVRSIgPSAiYmxhY2siLCAiRkFMU0UiID0gImdyZXk2MCIpKSArDQogIGdncGxvdDI6OnhsYWIoIkluZGl2aWR1YWwgSGV0ZXJvenlnb3NpdHkiKSArDQogIGdncGxvdDI6OnlsYWIoIkZyZXF1ZW5jeSIpICsNCiAgZ2dwbG90Mjo6Z2d0aXRsZSgiSW5kaXZpZHVhbCBIZXRlcm96eWdvc2l0eSAtIEJ5IFN0cmVhbSIpICsNCiAgZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTQpIA0KYGBgDQoNClJlbW92ZSBvdXRsaWVycy4NCmBgYHtyfQ0Kb3V0cHV0IDwtIGxhcHBseShzaWxseXZlYywgZnVuY3Rpb24oeCkgew0KICBHQ0xyOjpyZW1vdmVfaWRzKA0KICAgIHNpbGx5ID0geCwNCiAgICBJRHMgPSBpbmRfaGV0ICU+JSBkcGx5cjo6ZmlsdGVyKG91dGxpZXIsIHNpbGx5ID09IHgpICU+JSBkcGx5cjo6cHVsbChmaXNoX2lkKQ0KICApDQp9KQ0KDQptZXNzYWdlKHBhc3RlMCgiQSB0b3RhbCBvZiAiLCBpbmRfaGV0ICU+JSBkcGx5cjo6ZmlsdGVyKG91dGxpZXIpICU+JSBucm93KCksICIgaW5kaXZpZHVhbHMgd2VyZSByZW1vdmVkIGZyb20gc2lsbHlzIGluIHNpbGx5dmVjLiIpKQ0KYGBgDQoNCk5vdCB0b28gbWFueSBjb250YW1pbmF0ZWQgc2FtcGxlcyEgTW9zdCBsaWtlbHkgY3VsbGVkIGR1cmluZyBtaXNzaW5nIGxvY2kuDQpgYGB7cn0NCigNCiAgc2FtcGxlX3NpemVfcWEgPC0gc2FtcGxlX3NpemVfcWEgJT4lDQogICAgZHBseXI6Om11dGF0ZShoZXRlcm96eWdvc2l0eSA9IGdlbm90eXBlZCAtIG1pc3NpbmcgLSBHQ0xyOjpzaWxseV9uKHNpbGx5dmVjID0gc2lsbHl2ZWMpICU+JSBkcGx5cjo6cHVsbChuKSkNCikNCmBgYA0KDQpPdmVyYWxsIGByIGZvcm1hdChzdW0oc2FtcGxlX3NpemVfcWEkaGV0ZXJvenlnb3NpdHkpLCBiaWcubWFyayA9ICIsIilgIHNhbXBsZXMgZHJvcHBlZCBkdWUgdG8gc2FtcGxlIGNvbnRhbWluYXRpb24uIA0KDQpXaGF0IHdhcyBjb250YW1pbmF0aW9uIGJ5IHNwYXduaW5nIHN0YXRlPw0KYGBge3J9DQptZXRhZGF0YSAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoZ2Vub3R5cGVkKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoaGV0X291dGxpZXIgPSBzaWxseV9zb3VyY2UgJWluJSAoaW5kX2hldCAlPiUgZHBseXI6OmZpbHRlcihvdXRsaWVyKSAlPiUgZHBseXI6OnB1bGwoU2lsbHlTb3VyY2UpKSkgJT4lIA0KICBkcGx5cjo6Y291bnQoaGV0X291dGxpZXIsIHNwYXduaW5nX3N0YXRlKSAlPiUgDQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaGV0X291dGxpZXIsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICBkcGx5cjo6cmVuYW1lKG5fZHJvcCA9IGBUUlVFYCwgbl9rZWVwID0gYEZBTFNFYCkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKG4gPSBuX2tlZXAgKyBuX2Ryb3AsDQogICAgICAgICAgICAgICAgcF9rZWVwID0gbl9rZWVwIC8gbiwNCiAgICAgICAgICAgICAgICBzcGF3bmluZ19zdGF0ZSA9IGZhY3RvcihzcGF3bmluZ19zdGF0ZSwgbGV2ZWxzID0gYygiQWxpdmUiLCAiUGluayBHaWxsIiwgIkdyZXkgR2lsbCIsICJSb3R0aW5nIikpKSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKHNwYXduaW5nX3N0YXRlKQ0KYGBgDQoNCkFzIGV4cGVjdGVkLCBjb250YW1pbmF0aW9uIChoZXRlcm96eWdvc2l0eSBvdXRsaWVycykgd2FzIGhpZ2hlciBmb3Igcm90dGluZyBmaXNoLCBidXQgaG9uZXN0bHksIG5vdCB0ZXJyaWJsZSENCg0KIyMgRHVwbGljYXRlICg+PTk1JSkNCg0KSWRlbnRpZnkgcG90ZW50aWFsIGR1cGxpY2F0ZSBnZW5vdHlwZXMgKD49OTUlIGxvY2kpLg0KYGBge3J9DQpkdXBsaWNhdGVfY2hlY2tfOTUgPC0NCiAgR0NMcjo6ZHVwY2hlY2tfd2l0aGluX3NpbGx5KA0KICAgIHNpbGx5dmVjID0gc2lsbHl2ZWMsDQogICAgbWlucHJvcG9ydGlvbiA9IDAuOTUsDQogICAgbWlubm9ubWlzc2luZyA9IDAuNiwNCiAgICBuY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAtIDQNCiAgKQ0KDQpHQ0xyOjpzYXZlX29iamVjdHMoImR1cGxpY2F0ZV9jaGVja185NSIsIHBhdGggPSAib2JqZWN0cyIsIHJkcyA9IFRSVUUpDQpgYGANCg0KSG93IG1hbnkgZHVwbGljYXRlIHBhaXJzIGJ5IHNpbGx5Pw0KYGBge3J9DQpkdXBsaWNhdGVfY2hlY2tfOTUgJT4lIA0KICBkcGx5cjo6Y291bnQoc2lsbHkpICU+JSANCiAgZHBseXI6OmFycmFuZ2UoZHBseXI6OmRlc2MobikpDQpgYGANCg0KUmVtb3ZlICoqYm90aCoqIGR1cGxpY2F0ZXMhIEFzIG9wcG9zZWQgdG8gR1NJIHdvcmssIHdoZXJlIHdlIHdhbnQgdG8ga2VlcCBpbmRpdmlkdWFscyBidXQgYXJlbuKAmXQgdHlwaWNhbGx5IHdvcnJpZWQgYWJvdXQgcGFpcmVkIGRhdGEsIGhlcmUgd2Ugd2FudCB0byByZW1vdmUgYm90aCBpbmRpdmlkdWFscyBhcyB0aGUgcGFpcmVkIGRhdGEgaW50ZWdyaXR5IChpbmNsdWRpbmcgc2FtcGxlIGRhdGUsIG90b2xpdGggcmVhZHMsIGV0Yy4pIGlzIGxvc3QuDQpgYGB7cn0NCmR1cGxpY2F0ZXNfcmVtb3ZlZCA8LQ0KICBHQ0xyOjpyZW1vdmVfZHVwcyhkdXBjaGVjayA9IGR1cGxpY2F0ZV9jaGVja185NSwgcmVtb3ZlX2JvdGggPSBUUlVFKQ0KDQpHQ0xyOjpzYXZlX29iamVjdHMoImR1cGxpY2F0ZXNfcmVtb3ZlZCIsIHBhdGggPSAib2JqZWN0cyIsIHJkcyA9IFRSVUUpDQoNCigNCiAgc2FtcGxlX3NpemVfcWEgPC0gc2FtcGxlX3NpemVfcWEgJT4lDQogICAgZHBseXI6Om11dGF0ZSgNCiAgICAgIGR1cGxpY2F0ZSA9IGdlbm90eXBlZCAtIG1pc3NpbmcgLSBoZXRlcm96eWdvc2l0eSAtIEdDTHI6OnNpbGx5X24oc2lsbHl2ZWMgPSBzaWxseXZlYykgJT4lIGRwbHlyOjpwdWxsKG4pDQogICAgKQ0KKQ0KYGBgDQoNCk92ZXJhbGwgYHIgZm9ybWF0KHN1bShzYW1wbGVfc2l6ZV9xYSRkdXBsaWNhdGUpLCBiaWcubWFyayA9ICIsIilgIHNhbXBsZXMgZHJvcHBlZCBkdWUgdG8gZHVwbGljYXRlIGdlbm90eXBlcy4gDQoNCiMjIEZpbmFsDQoNCkZpbmFsLCBwb3N0LVFBIHNhbXBsZSBzaXplcyBieSBzaWxseS4NCmBgYHtyfQ0KKA0KICBzYW1wbGVfc2l6ZV9xYSA8LSBzYW1wbGVfc2l6ZV9xYSAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKA0KICAgICAgZmluYWwgPSAgR0NMcjo6c2lsbHlfbihzaWxseXZlYyA9IHNpbGx5dmVjKSAlPiUgZHBseXI6OnB1bGwobikNCiAgICApDQopDQoNCkdDTHI6OnNhdmVfb2JqZWN0cygic2FtcGxlX3NpemVfcWEiLCBwYXRoID0gIm9iamVjdHMiLCByZHMgPSBUUlVFKQ0KDQpmczo6ZGlyX2NyZWF0ZSgib3V0cHV0IikNCnJlYWRyOjp3cml0ZV9jc3YoeCA9IHNhbXBsZV9zaXplX3FhLCBmaWxlID0gIm91dHB1dC9zYW1wbGVfc2l6ZV9xYS5jc3YiKQ0KYGBgDQoNCiMgU2F2ZQ0KDQpTYXZlIGZpbmFsIGdlbm90eXBlcyBhbmQgdXBkYXRlIG1ldGFkYXRhLg0KDQojIyBHZW5vdHlwZXMNCg0KU2F2ZSBwb3N0LVFBIGdlbm90eXBlcyB3aXRoIGpvaW5lZCBtZXRhZGF0YS4NCmBgYHtyfQ0KZnM6OmRpcl9jcmVhdGUoImRhdGEvZ2Vub3R5cGVzL3Bvc3RRQSIpDQpHQ0xyOjpzYXZlX3NpbGx5cyhzaWxseXZlYyA9IHNpbGx5dmVjLCBwYXRoID0gImRhdGEvZ2Vub3R5cGVzL3Bvc3RRQSIsIHJkcyA9IFRSVUUpDQpgYGANCg0KIyMgTWV0YWRhdGENCg0KYWRkIGBwYXNzX1FBYA0KYGBge3J9DQpwb3N0UUFfc2lsbHlfc291cmNlIDwtIHNhcHBseShzaWxseXZlYywgZnVuY3Rpb24oc2lsbHkpIHtnZXQocGFzdGUwKHNpbGx5LCAiLmdjbCIpKSRTaWxseVNvdXJjZX0pICU+JSB1bmxpc3QoKQ0KDQptZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUgDQogIGRwbHlyOjptdXRhdGUocGFzc19RQSA9IHNpbGx5X3NvdXJjZSAlaW4lIHBvc3RRQV9zaWxseV9zb3VyY2UpDQpgYGANCg0KQ29uZmlybS4NCmBgYHtyfQ0KbWV0YWRhdGEgJT4lIA0KICBkcGx5cjo6Y291bnQoZ2Vub3R5cGVkLCBwYXNzX1FBKQ0KYGBgDQoNCioqTk9URSoqIHRoYXQgYGdlbm90eXBlZGAgaW4gYHNhbXBsZV9zaXplX3FhYCBkb2Vzbid0IG1hdGNoIGBnZW5vdHlwZWRgIGluIGBtZXRhZGF0YWAgYmVjYXVzZSBJIHByZS1maWx0ZXJlZCBvdXQgUUMgQ3Jvc3NjaGVjayBwbGF0ZXMuDQpgYGB7cn0NCnNhbXBsZV9zaXplX3FhICU+JSANCiAgamFuaXRvcjo6YWRvcm5fdG90YWxzKCkgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNpbGx5ID09ICJUb3RhbCIpDQpgYGANCg0KIyBRQSBDb25jbHVzaW9ucw0KDQpPdmVyYWxsIGByIGZvcm1hdChzdW0obWV0YWRhdGEkZ2Vub3R5cGVkKSwgYmlnLm1hcmsgPSAiLCIpYCB0b3RhbCBwZWRpZ3JlZSBzYW1wbGVzIHdlcmUgZ2Vub3R5cGVkOyBgciBmb3JtYXQoc3VtKG1ldGFkYXRhJGdlbm90eXBlZCkgLSBzdW0oc2FtcGxlX3NpemVfcWEkZ2Vub3R5cGVkKSwgYmlnLm1hcmsgPSAiLCIpYCB3ZXJlIHJlbW92ZWQgZHVlIHRvIHBlbmRpbmcgUUMgQ3Jvc3NjaGVja3M7IGByIGZvcm1hdChzdW0oc2FtcGxlX3NpemVfcWEkbWlzc2luZyksIGJpZy5tYXJrID0gIiwiKWAgd2VyZSByZW1vdmVkIGR1ZSB0byBwb29yIHF1YWxpdHkgRE5BIChtaXNzaW5nID49IDIwJSBsb2NpKTsgYHIgZm9ybWF0KHN1bShzYW1wbGVfc2l6ZV9xYSRoZXRlcm96eWdvc2l0eSksIGJpZy5tYXJrID0gIiwiKWAgd2VyZSByZW1vdmVkIGR1ZSB0byBjb250YW1pbmF0ZWQgRE5BIChoZXRlcm96eWdvc2l0eSA+PSAzLjUgbW9kaWZpZWQgei1zY29yZSk7IGFuZCBgciBmb3JtYXQoc3VtKHNhbXBsZV9zaXplX3FhJGR1cGxpY2F0ZSksIGJpZy5tYXJrID0gIiwiKWAgd2VyZSByZW1vdmVkIGR1ZSB0byBkdXBsaWNhdGUgZ2Vub3R5cGVzICg+PSA5NSUgbG9jaSksIGxlYXZpbmcgYSB0b3RhbCBvZiAqKmByIGZvcm1hdChzdW0oc2FtcGxlX3NpemVfcWEkZmluYWwpLCBiaWcubWFyayA9ICIsIilgKiogaW4gdGhlIGZpbmFsIGRhdGFzZXQuDQoNCiMgQWRhcHRpdmUgTG9jaSBHZW5vdHlwZXMNCg0KQ3JlYXRlIGEgc2luZ2xlIG9iamVjdCB3aXRoIGp1c3QgdGhlIG1ldGFkYXRhIGFuZCBhZGFwdGl2ZSBsb2NpIGdlbm90eXBlcyBmb3IgTk9BQSBBQkwuDQpgYGB7cn0NCigNCiAgY2h1bV9hZGFwdGl2ZV9nZW5vIDwtIHNhcHBseShzaWxseXZlYywgZnVuY3Rpb24oc2lsbHkpIHsNCiAgICBnZXQocGFzdGUwKHNpbGx5LCAiLmdjbCIpKQ0KICB9LCBzaW1wbGlmeSA9IEZBTFNFKSAlPiUNCiAgICBkcGx5cjo6YmluZF9yb3dzKCkgJT4lICAjIGJpbmQgYWxsIC5nY2wgb2JqZWN0cyBmcm9tIHNlcGFyYXRlIHNpbGx5cyB0b2dldGhlcg0KICAgIGRwbHlyOjpzZWxlY3QoDQogICAgICBzaWxseV9zb3VyY2UgPSBTaWxseVNvdXJjZSwNCiAgICAgIHBsYXRlX2lkID0gUExBVEVfSUQsDQogICAgICBzdHJlYW06c3dfYWdlLA0KICAgICAgZHBseXI6OmNvbnRhaW5zKCJPa2VWMiIpDQogICAgKSAgIyBrZWVwIGZpZWxkIGRhdGEgYW5kIGFkYXB0aXZlIGxvY2kNCikNCmBgYA0KDQojIyBDYWxjdWxhdGUgQWxsZWxlIEZyZXF1ZW5jaWVzDQoNCkknbSBnb2luZyB0byBsb29rIGF0IHRoaXMgdGhyZWUgZGlmZmVyZW50IHdheXM6ICANCg0KICAxKSBuYWl2ZSwgYWxsIHNhbXBsZXMgKGluY2x1ZGluZyBoYXRjaGVyeSBzdHJheXMpIHdpdGggY29sbGVjdGlvbiBkYXRlLA0KICAyKSBuYXR1cmFsLW9yaWdpbiBmaXNoIG9ubHkgKG5vIGhhdGNoZXJ5IHN0cmF5cykgd2l0aCBjb2xsZWN0aW9uIGRhdGUsIGFuZA0KICAzKSBuYXR1cmFsLW9yaWdpbiBmaXNoIG9ubHkgKG5vIGhhdGNoZXJ5IHN0cmF5cykgd2l0aCAqYWRqdXN0ZWQqIGNvbGxlY3Rpb24gZGF0ZSBiYXNlZCBvbiBgc3Bhd25pbmdfc3RhdGVgIChyZS1zY2FsaW5nIHRvICpBbGl2ZSopDQogICAgKiBBbGl2ZSA9IGNvbGxlY3Rpb24gZGF0ZQ0KICAgICogUGluayBHaWxsID0gY29sbGVjdGlvbiBkYXRlIC0gMiBkYXlzDQogICAgKiBHcmV5IEdpbGwgPSBjb2xsZWN0aW9uIGRhdGUgLSA1IGRheXMNCiAgICAqIFJvdHRpbmcgPSBjb2xsZWN0aW9uIGRhdGUgLSAxMCBkYXlzDQoNClN0cmVhbSBzYW1wbGluZyB3YXMgbGltaXRlZCBieSBib3RoIGNyZXcgYXZhaWxhYmlsaXR5IGFuZCB3ZWF0aGVyIGNvbmRpdGlvbnMgKGZsb29kaW5nKSwgc28gY29sbGVjdGlvbiBkYXRlIGlzIG5vdCBhIHBlcmZlY3QgcHJveHkgZm9yIHJ1biB0aW1pbmcuIEFkZGl0aW9uYWxseSwgaW4gc29tZSBvZiB0aGUgbGF0ZXIgeWVhcnMgY3Jld3Mgd2VyZSB0YWdnaW5nIGxpdmUgZmlzaCBuZWFyIHRoZSBpbnRlcnRpZGFsIHRvIGJvb3N0IGdlbmV0aWMgc2FtcGxlIHNpemU7IHNvIHRoZXNlIGxpdmUgZmlzaCB3ZXJlIHNhbXBsZWQgbXVjaCBlYXJsaWVyIHRoYW4gcG9zdC1zcGF3biBjYXJjYXNzZXMgd2l0aCB0aGUgc2FtZSBydW4gdGltaW5nLg0KDQojIyMgQWxsIFNhbXBsZXMgKEhhdGNoZXJ5ICsgTmF0dXJhbCkNCg0KQ2FsY3VsYXRlIHRoZSBhbGxlbGUgY291bnRzIGZvciBlYWNoIGxvY3VzIGJ5IHN0cmVhbSwgeWVhciwgYW5kIERPWS4NCkFkYXB0ZWQgZnJvbSBQYXQgQmFycnkncyBjb2RlIGluIGBBSFJQX0NodW1BZGFwdGl2ZS5odG1sYA0KYGBge3J9DQpsb2NpX2FkYXB0aXZlIDwtIGdyZXAocGF0dGVybiA9ICJPa2VWMiIsIHggPSBsb2NpMjUxLCB2YWx1ZSA9IFRSVUUpDQpHQ0xyOjpzYXZlX29iamVjdHMob2JqZWN0cyA9ICJsb2NpX2FkYXB0aXZlIiwgcGF0aCA9ICJvYmplY3RzLyIpDQoNCiMgdGVzdGluZw0KIyBjaHVtX2FkYXB0aXZlX2dlbm8gJT4lDQojICAgZHBseXI6OnNlbGVjdChzaWxseV9zb3VyY2UsIHN0cmVhbSwgeWVhciwgRE9ZLCB0aWR5c2VsZWN0OjpzdGFydHNfd2l0aChsb2NpX2FkYXB0aXZlWzFdKSkgJT4lDQojICAgdGlkeXI6OnBpdm90X2xvbmdlcihjb2xzID0gdGlkeXNlbGVjdDo6c3RhcnRzX3dpdGgobG9jaV9hZGFwdGl2ZVsxXSksIG5hbWVzX3RvID0gImxvY3VzIiwgdmFsdWVzX3RvID0gImFsbGVsZSIpICU+JQ0KIyAgIGRwbHlyOjptdXRhdGUoYWxsZWxlID0gZmFjdG9yKGFsbGVsZSwgbGV2ZWxzID0gTG9jdXNDb250cm9sJGFsbGVsZXNbW2xvY2lfYWRhcHRpdmVbMV1dXSRjYWxsKSwNCiMgICAgICAgICAgICAgICAgIGFsbGVsZSA9IGFzLm51bWVyaWMoYWxsZWxlKSkgJT4lICAjIG1ha2UgZmFjdG9yIGFuZCBjb252ZXJ0IHRvIG51bWVyaWMNCiMgICBkcGx5cjo6Y291bnQoc3RyZWFtLCB5ZWFyLCBET1ksIGFsbGVsZSkgJT4lDQojICAgZHBseXI6Om11dGF0ZShsb2N1cyA9IGxvY2lfYWRhcHRpdmVbMV0pDQoNCiMgY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZWFjaCBhbGxlbGUgb2JzZXJ2ZWQgcGVyIHN0cmVhbSwgeWVhciwgZGF5IGJ5IGxvb3BpbmcgdGhyb3VnaCBlYWNoIGxvY3VzDQojIE5PVEU6IHRoZXJlIGlzIG5vIGZpbHRlcmluZyBmb3IgaGF0Y2hlcnkvbmF0dXJhbCBvcmlnaW4NCihjaHVtX2FkYXB0aXZlX2FsbGVsZV9jb3VudHMgPC0gbGFwcGx5KGxvY2lfYWRhcHRpdmUsIGZ1bmN0aW9uKGxvY3VzKSB7DQogIGNodW1fYWRhcHRpdmVfZ2VubyAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KHNpbGx5X3NvdXJjZSwNCiAgICAgICAgICAgICAgICAgIHN0cmVhbSwNCiAgICAgICAgICAgICAgICAgIHllYXIsDQogICAgICAgICAgICAgICAgICBET1ksDQogICAgICAgICAgICAgICAgICB0aWR5c2VsZWN0OjpzdGFydHNfd2l0aChsb2N1cykpICU+JQ0KICAgIHRpZHlyOjpwaXZvdF9sb25nZXIoDQogICAgICBjb2xzID0gdGlkeXNlbGVjdDo6c3RhcnRzX3dpdGgobG9jdXMpLA0KICAgICAgbmFtZXNfdG8gPSAibG9jdXMiLA0KICAgICAgdmFsdWVzX3RvID0gImFsbGVsZSINCiAgICApICU+JQ0KICAgIGRwbHlyOjptdXRhdGUoYWxsZWxlID0gZmFjdG9yKGFsbGVsZSwgbGV2ZWxzID0gTG9jdXNDb250cm9sJGFsbGVsZXNbW3t7bG9jdXN9fV1dJGNhbGwpLCAgIyBvcmRlciBhbGxlbGVzIGFzIHBlciBMb2N1c0NvbnRyb2wNCiAgICAgICAgICAgICAgICAgIGFsbGVsZSA9IGFzLm51bWVyaWMoYWxsZWxlKSkgJT4lICAjIG1ha2UgZmFjdG9yIGFuZCBjb252ZXJ0IHRvIG51bWVyaWMNCiAgICBkcGx5cjo6Y291bnQoc3RyZWFtLCB5ZWFyLCBET1ksIGFsbGVsZSkgJT4lDQogICAgZHBseXI6Om11dGF0ZShsb2N1cyA9IGxvY3VzKQ0KfSkgJT4lDQogICAgZHBseXI6OmJpbmRfcm93cygpKQ0KYGBgDQoNCkNvbnZlcnQgdG8gYWxsZWxlIGZyZXF1ZW5jaWVzLg0KYGBge3J9DQooDQogIGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXEgPC0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfY291bnRzICU+JQ0KICAgIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gYWxsZWxlLCB2YWx1ZXNfZnJvbSA9IG4pICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoLWBOQWApICU+JSAgIyBkcm9wIG5vLWNhbGxzDQogICAgZHBseXI6Om11dGF0ZSgNCiAgICAgIGAxYCA9IHJlcGxhY2VfbmEoYDFgLCAwKSwNCiAgICAgIGAyYCA9IHJlcGxhY2VfbmEoYDJgLCAwKQ0KICAgICkgJT4lICAjIGFjY291bnQgZm9yIHVub2JzZXJ2ZWQgYWxsZWxlcyBvbiBhIGdpdmVuIGRheQ0KICAgIHRpZHlyOjpwaXZvdF9sb25nZXIoDQogICAgICBjb2xzID0gYDFgOmAyYCwNCiAgICAgIG5hbWVzX3RvID0gImFsbGVsZSIsDQogICAgICB2YWx1ZXNfdG8gPSAibiINCiAgICApICU+JQ0KICAgIGRwbHlyOjpncm91cF9ieShzdHJlYW0sIHllYXIsIERPWSwgbG9jdXMpICU+JQ0KICAgIGRwbHlyOjptdXRhdGUoDQogICAgICBuX2FsbGVsZXMgPSBzdW0obiksDQogICAgICBwX2FsbGVsZXMgPSBuIC8gbl9hbGxlbGVzLA0KICAgICAgU05QID0gcGFzdGUobG9jdXMsIGFsbGVsZSwgc2VwID0gIl8iKQ0KICAgICkgJT4lIA0KICAgIGRwbHlyOjp1bmdyb3VwKCkgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIobl9hbGxlbGVzID4gMCkgICMgcmVtb3ZlIGFueSBzdHJlYW0veWVhci9ET1kvbG9jdXMgY29tYmluYXRpb25zIHdpdGhvdXQgZGF0YQ0KKQ0KYGBgDQoNCkNvbmZpcm0gZGF0YSBzdHJ1Y3R1cmUuDQpgYGB7cn0NCmNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXEgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkobG9jdXMpICU+JSANCiAgZHBseXI6OnN1bW1hcmlzZShtaW4ocF9hbGxlbGVzKSwNCiAgICAgICAgICAgICAgICAgICBtYXgocF9hbGxlbGVzKSkNCg0KY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcSAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoaXMubmEocF9hbGxlbGVzKSkNCmBgYA0KDQojIyMgTmF0dXJhbC1vcmlnaW4gU2FtcGxlcyAobm8gSGF0Y2hlcnkgU3RyYXlzKQ0KDQpSZS1jYWxjdWxhdGUgdGhlIGFsbGVsZSBjb3VudHMgZm9yICoqbmF0dXJhbC1vcmlnaW4qKiBmaXNoIG9ubHkgZm9yIGVhY2ggbG9jdXMgYnkgc3RyZWFtLCB5ZWFyLCBhbmQgRE9ZLiBObyBoYXRjaGVyeSBzdHJheXMhDQpBZGFwdGVkIGZyb20gUGF0IEJhcnJ5J3MgY29kZSBpbiBgQUhSUF9DaHVtQWRhcHRpdmUuaHRtbGANCmBgYHtyfQ0KIyBjYWxjdWxhdGUgdGhlIG51bWJlciBvZiBlYWNoIGFsbGVsZSBvYnNlcnZlZCBwZXIgc3RyZWFtLCB5ZWFyLCBkYXkgYnkgbG9vcGluZyB0aHJvdWdoIGVhY2ggbG9jdXMNCiMgTk9URTogcmVtb3ZpbmcgYWxsIGhhdGNoZXJ5LW9yaWdpbiBzdHJheXMhISENCihjaHVtX2FkYXB0aXZlX2FsbGVsZV9jb3VudHNfd2lsZCA8LSBsYXBwbHkobG9jaV9hZGFwdGl2ZSwgZnVuY3Rpb24obG9jdXMpIHsNCiAgY2h1bV9hZGFwdGl2ZV9nZW5vICU+JQ0KICAgIGRwbHlyOjpmaWx0ZXIob3JpZ2luID09ICJOYXR1cmFsIikgJT4lICAjIG5vIGhhdGNoZXJ5IHN0cmF5cyBvciB1bmtub3duIG9yaWdpbiBmaXNoDQogICAgZHBseXI6OnNlbGVjdChzaWxseV9zb3VyY2UsDQogICAgICAgICAgICAgICAgICBzdHJlYW0sDQogICAgICAgICAgICAgICAgICB5ZWFyLA0KICAgICAgICAgICAgICAgICAgRE9ZLA0KICAgICAgICAgICAgICAgICAgdGlkeXNlbGVjdDo6c3RhcnRzX3dpdGgobG9jdXMpKSAlPiUNCiAgICB0aWR5cjo6cGl2b3RfbG9uZ2VyKA0KICAgICAgY29scyA9IHRpZHlzZWxlY3Q6OnN0YXJ0c193aXRoKGxvY3VzKSwNCiAgICAgIG5hbWVzX3RvID0gImxvY3VzIiwNCiAgICAgIHZhbHVlc190byA9ICJhbGxlbGUiDQogICAgKSAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKGFsbGVsZSA9IGZhY3RvcihhbGxlbGUsIGxldmVscyA9IExvY3VzQ29udHJvbCRhbGxlbGVzW1t7e2xvY3VzfX1dXSRjYWxsKSwgICMgb3JkZXIgYWxsZWxlcyBhcyBwZXIgTG9jdXNDb250cm9sDQogICAgICAgICAgICAgICAgICBhbGxlbGUgPSBhcy5udW1lcmljKGFsbGVsZSkpICU+JSAgIyBtYWtlIGZhY3RvciBhbmQgY29udmVydCB0byBudW1lcmljDQogICAgZHBseXI6OmNvdW50KHN0cmVhbSwgeWVhciwgRE9ZLCBhbGxlbGUpICU+JQ0KICAgIGRwbHlyOjptdXRhdGUobG9jdXMgPSBsb2N1cykNCn0pICU+JQ0KICAgIGRwbHlyOjpiaW5kX3Jvd3MoKSkNCmBgYA0KDQpDb252ZXJ0IHRvIGFsbGVsZSBmcmVxdWVuY2llcy4NCmBgYHtyfQ0KKA0KICBjaHVtX2FkYXB0aXZlX2FsbGVsZV9mcmVxX3dpbGQgPC0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfY291bnRzX3dpbGQgJT4lDQogICAgdGlkeXI6OnBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBhbGxlbGUsIHZhbHVlc19mcm9tID0gbikgJT4lDQogICAgZHBseXI6OnNlbGVjdCgtYE5BYCkgJT4lICAjIGRyb3Agbm8tY2FsbHMNCiAgICBkcGx5cjo6bXV0YXRlKA0KICAgICAgYDFgID0gcmVwbGFjZV9uYShgMWAsIDApLA0KICAgICAgIyBhY2NvdW50IGZvciB1bm9ic2VydmVkIGFsbGVsZXMgb24gYSBnaXZlbiBkYXkNCiAgICAgIGAyYCA9IHJlcGxhY2VfbmEoYDJgLCAwKQ0KICAgICkgJT4lDQogICAgdGlkeXI6OnBpdm90X2xvbmdlcigNCiAgICAgIGNvbHMgPSBgMWA6YDJgLA0KICAgICAgbmFtZXNfdG8gPSAiYWxsZWxlIiwNCiAgICAgIHZhbHVlc190byA9ICJuIg0KICAgICkgJT4lDQogICAgZHBseXI6Omdyb3VwX2J5KHN0cmVhbSwgeWVhciwgRE9ZLCBsb2N1cykgJT4lDQogICAgZHBseXI6Om11dGF0ZSgNCiAgICAgIG5fYWxsZWxlcyA9IHN1bShuKSwNCiAgICAgIHBfYWxsZWxlcyA9IG4gLyBuX2FsbGVsZXMsDQogICAgICBTTlAgPSBwYXN0ZShsb2N1cywgYWxsZWxlLCBzZXAgPSAiXyIpDQogICAgKSAlPiUgDQogICAgZHBseXI6OnVuZ3JvdXAoKSAlPiUgDQogICAgZHBseXI6OmZpbHRlcihuX2FsbGVsZXMgPiAwKSAgIyByZW1vdmUgYW55IHN0cmVhbS95ZWFyL0RPWS9sb2N1cyBjb21iaW5hdGlvbnMgd2l0aG91dCBkYXRhDQopDQpgYGANCg0KIyMjIEFkanVzdCBmb3IgU3Bhd25pbmcgU3RhdGUNCg0KUmUtY2FsY3VsYXRlIHRoZSBhbGxlbGUgY291bnRzIGZvciAqKm5hdHVyYWwtb3JpZ2luKiogZmlzaCBvbmx5IGZvciBlYWNoIGxvY3VzIGJ5IHN0cmVhbSwgeWVhciwgYW5kIERPWS4gTm8gaGF0Y2hlcnkgc3RyYXlzISBBZGp1c3QgRE9ZIGJhc2VkIG9uIHNwYXduaW5nIHN0YXRlLCByZS1zY2FsaW5nIHRvICpBbGl2ZSouIEFkanVzdG1lbnRzIGFyZSAqKnZlcnJycnkqKiByb3VnaCBndWVzc2VzLg0KDQogICogUm90dGluZyA9IEFsaXZlICsgMTAgZGF5cw0KICAqIEdyZXkgR2lsbCA9IEFsaXZlICsgNSBkYXlzDQogICogUGluayBHaWxsID0gQWxpdmUgKyAyIGRheXMNCg0KQWRhcHRlZCBmcm9tIFBhdCBCYXJyeSdzIGNvZGUgaW4gYEFIUlBfQ2h1bUFkYXB0aXZlLmh0bWxgDQpgYGB7cn0NCiMgY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZWFjaCBhbGxlbGUgb2JzZXJ2ZWQgcGVyIHN0cmVhbSwgeWVhciwgZGF5IGJ5IGxvb3BpbmcgdGhyb3VnaCBlYWNoIGxvY3VzDQojIE5PVEU6IHJlbW92aW5nIGFsbCBoYXRjaGVyeS1vcmlnaW4gc3RyYXlzISEhDQooY2h1bV9hZGFwdGl2ZV9hbGxlbGVfY291bnRzX3dpbGRfYWRqX3NwYXduaW5nIDwtIGxhcHBseShsb2NpX2FkYXB0aXZlLCBmdW5jdGlvbihsb2N1cykgew0KICBjaHVtX2FkYXB0aXZlX2dlbm8gJT4lDQogICAgZHBseXI6OmZpbHRlcihvcmlnaW4gPT0gIk5hdHVyYWwiKSAlPiUgICMgbm8gaGF0Y2hlcnkgc3RyYXlzIG9yIHVua25vd24gb3JpZ2luIGZpc2gNCiAgICBkcGx5cjo6bXV0YXRlKERPWSA9IGRwbHlyOjpjYXNlX3doZW4oc3Bhd25pbmdfc3RhdGUgPT0gIlBpbmsgR2lsbCIgfiBET1kgLSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGF3bmluZ19zdGF0ZSA9PSAiR3JleSBHaWxsIiB+IERPWSAtIDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYXduaW5nX3N0YXRlID09ICJSb3R0aW5nIiB+IERPWSAtIDEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gRE9ZDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICU+JSAgIyBhZGp1c3QgRE9ZIGJhc2VkIG9uIHNwYXduaW5nIGRhdGUsIGFkanVzdG1lbnRzIGFyZSBjb21wbGV0ZSBXQUdzISEhDQogICAgZHBseXI6OnNlbGVjdChzaWxseV9zb3VyY2UsDQogICAgICAgICAgICAgICAgICBzdHJlYW0sDQogICAgICAgICAgICAgICAgICB5ZWFyLA0KICAgICAgICAgICAgICAgICAgRE9ZLA0KICAgICAgICAgICAgICAgICAgdGlkeXNlbGVjdDo6c3RhcnRzX3dpdGgobG9jdXMpKSAlPiUNCiAgICB0aWR5cjo6cGl2b3RfbG9uZ2VyKA0KICAgICAgY29scyA9IHRpZHlzZWxlY3Q6OnN0YXJ0c193aXRoKGxvY3VzKSwNCiAgICAgIG5hbWVzX3RvID0gImxvY3VzIiwNCiAgICAgIHZhbHVlc190byA9ICJhbGxlbGUiDQogICAgKSAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKGFsbGVsZSA9IGZhY3RvcihhbGxlbGUsIGxldmVscyA9IExvY3VzQ29udHJvbCRhbGxlbGVzW1t7e2xvY3VzfX1dXSRjYWxsKSwgICMgb3JkZXIgYWxsZWxlcyBhcyBwZXIgTG9jdXNDb250cm9sDQogICAgICAgICAgICAgICAgICBhbGxlbGUgPSBhcy5udW1lcmljKGFsbGVsZSkpICU+JSAgIyBtYWtlIGZhY3RvciBhbmQgY29udmVydCB0byBudW1lcmljDQogICAgZHBseXI6OmNvdW50KHN0cmVhbSwgeWVhciwgRE9ZLCBhbGxlbGUpICU+JQ0KICAgIGRwbHlyOjptdXRhdGUobG9jdXMgPSBsb2N1cykNCn0pICU+JQ0KICAgIGRwbHlyOjpiaW5kX3Jvd3MoKSkNCmBgYA0KDQpDb252ZXJ0IHRvIGFsbGVsZSBmcmVxdWVuY2llcy4NCmBgYHtyfQ0KKA0KICBjaHVtX2FkYXB0aXZlX2FsbGVsZV9mcmVxX3dpbGRfYWRqX3NwYXduaW5nIDwtIGNodW1fYWRhcHRpdmVfYWxsZWxlX2NvdW50c193aWxkX2Fkal9zcGF3bmluZyAlPiUNCiAgICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGFsbGVsZSwgdmFsdWVzX2Zyb20gPSBuKSAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KC1gTkFgKSAlPiUgICMgZHJvcCBuby1jYWxscw0KICAgIGRwbHlyOjptdXRhdGUoDQogICAgICBgMWAgPSByZXBsYWNlX25hKGAxYCwgMCksDQogICAgICAjIGFjY291bnQgZm9yIHVub2JzZXJ2ZWQgYWxsZWxlcyBvbiBhIGdpdmVuIGRheQ0KICAgICAgYDJgID0gcmVwbGFjZV9uYShgMmAsIDApDQogICAgKSAlPiUNCiAgICB0aWR5cjo6cGl2b3RfbG9uZ2VyKA0KICAgICAgY29scyA9IGAxYDpgMmAsDQogICAgICBuYW1lc190byA9ICJhbGxlbGUiLA0KICAgICAgdmFsdWVzX3RvID0gIm4iDQogICAgKSAlPiUNCiAgICBkcGx5cjo6Z3JvdXBfYnkoc3RyZWFtLCB5ZWFyLCBET1ksIGxvY3VzKSAlPiUNCiAgICBkcGx5cjo6bXV0YXRlKA0KICAgICAgbl9hbGxlbGVzID0gc3VtKG4pLA0KICAgICAgcF9hbGxlbGVzID0gbiAvIG5fYWxsZWxlcywNCiAgICAgIFNOUCA9IHBhc3RlKGxvY3VzLCBhbGxlbGUsIHNlcCA9ICJfIikNCiAgICApICU+JSANCiAgICBkcGx5cjo6dW5ncm91cCgpICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKG5fYWxsZWxlcyA+IDApICAjIHJlbW92ZSBhbnkgc3RyZWFtL3llYXIvRE9ZL2xvY3VzIGNvbWJpbmF0aW9ucyB3aXRob3V0IGRhdGENCikNCmBgYA0KDQojIyBWaWV3IEFsbGVsZSBGcmVxdWVuY2llcw0KDQpDcmVhdGUgZnVuY3Rpb25zIHRvIHN0YW5kYXJkaXplIHBsb3RzLiBFYWNoIHNldCBvZiBwbG90cyBpcyBmYWNldHRlZCB0byBzaG93IHllYXJzL2xvY2kuDQoqKk5PVEUqKiBzaW5jZSBJIGFtIGZvcmNpbmcgdGhlIHktYXhlcyB0byAwLTEsIHRoZSBTRSByaWJib25zIGdldCBjdXQgb2ZmIGZvciBzb21lIG9mIHRoZSBmaXQgbGluZXMuDQpgYGB7cn0NCmFkYXB0aXZlX2ZyZXFfcGxvdCA8LSBmdW5jdGlvbihmcmVxLCBzdHJlYW0pIHsNCiAgZnJlcSAlPiUgDQogICAgZHBseXI6OmZpbHRlcihzdHJlYW0gPT0ge3tzdHJlYW19fSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUobG9jdXMgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlX2FsbChzdHJpbmcgPSBsb2N1cywgcGF0dGVybiA9ICJPa2VWMl8iKSkgJT4lICAjIHNob3J0ZW4gbG9jdXMgbmFtZXMgdG8gZml0DQogICAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gRE9ZLCB5ID0gcF9hbGxlbGVzLCBjb2xvdXIgPSBTTlAsIGdyb3VwX2J5KFNOUCkpKSArDQogICAgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpICsNCiAgICBnZ3Bsb3QyOjpnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCB3ZWlnaHQgPSAibl9hbGxlbGVzIiwgZm9ybXVsYSA9IHl+eCkgKw0KICAgIGdncGxvdDI6OnlsaW0oMCwgMSkgKyAgIyB0aGlzIGN1dHMgb2ZmIHRoZSBTRSdzIGZvciB0aGUgZml0IGxpbmUNCiAgICBnZ3Bsb3QyOjpmYWNldF9ncmlkKGxvY3VzIH4geWVhcikgKyANCiAgICBnZ3Bsb3QyOjp0aGVtZV9idygpICsgDQogICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDAuNSkpICsNCiAgICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gc3RyZWFtLCB4ID0gIkRPWSIsIHkgPSAiQWxsZWxlIEZyZXF1ZW5jeSIpDQp9DQoNCmF2Z19hbGxlbGVfbl9wbG90IDwtIGZ1bmN0aW9uKGZyZXEsIHN0cmVhbSkgew0KICBmcmVxICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKHN0cmVhbSA9PSB7e3N0cmVhbX19KSAlPiUgDQogICAgZHBseXI6Omdyb3VwX2J5KHN0cmVhbSwgeWVhciwgRE9ZKSAlPiUgDQogICAgZHBseXI6OnN1bW1hcmlzZShuX2FsbGVsZXMgPSByb3VuZChtZWFuKG5fYWxsZWxlcykpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUgDQogICAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gRE9ZLCB5ID0gbl9hbGxlbGVzKSkgKyANCiAgICBnZ3Bsb3QyOjpnZW9tX2NvbCgpICsNCiAgICBnZ3Bsb3QyOjpmYWNldF9ncmlkKH55ZWFyKSArDQogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHBhc3RlMChzdHJlYW0sICIgLSBBdmVyYWdlIEFsbGVsZSBTYW1wbGUgU2l6ZSBwZXIgRGF5IiksIHkgPSAiQXZlcmFnZSBOdW1iZXIgb2YgQWxsZWxlcyIpICsNCiAgICBnZ3Bsb3QyOjp0aGVtZV9idygpICsNCiAgICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAwLjUpKQ0KfQ0KYGBgDQoNCiMjIyBBbGwgU2FtcGxlcw0KDQpJbmNsdWRlcyBoYXRjaGVyeS1vcmlnaW4gc3RyYXlzIGFuZCB1bmtub3duIG9yaWdpbiBmaXNoIChpLmUuIG5vIG90b2xpdGggcmVhZCkuDQoNCiMjIyMgQWRtaXJhbHR5DQoNClNob3cgYXZlcmFnZSBhbGxlbGUgc2FtcGxlIHNpemVzIHBlciBET1kgYnkgeWVhci4NCmBgYHtyfQ0KYXZnX2FsbGVsZV9uX3Bsb3QoZnJlcSA9IGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXEsIHN0cmVhbSA9ICJBZG1pcmFsdHkiKQ0KYGBgDQoNCioqTk9URSoqIHNtYWxsIHNhbXBsZSBzaXplcyBpbiAyMDE4IGFuZCBsYXRlciBzYW1wbGluZyBkYXRlcyBkdWUgdG8gZmxvb2RpbmcgY29uZGl0aW9ucy4NCg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCmFkYXB0aXZlX2ZyZXFfcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcSwgc3RyZWFtID0gIkFkbWlyYWx0eSIpDQpgYGANCg0KSW5jb25zaXN0ZW50IHBhdHRlcm5zIGFtb25nIHllYXJzIGZvciBtb3N0IGxvY2kuIFRoZSBtb3N0IGNvbnNpc3RlbnQgcmVsYXRpb25zaGlwIGlzIGluIGBMRzI5XzI1NTE1MTYxYC4gU29tZSBldmlkZW5jZSBvZiBhIGJyb29kIHllYXIgZWZmZWN0IHdpdGggbW9yZSBzaW1pbGFyaXRpZXMgYmV0d2VlbiAyMDEzLzIwMTcgKGFzc3VtaW5nIG1vc3QgY2h1bSBhcmUgYWdlIDQpLg0KDQojIyMjIEZpc2gNCg0KU2hvdyBhdmVyYWdlIGFsbGVsZSBzYW1wbGUgc2l6ZXMgcGVyIERPWSBieSB5ZWFyLg0KYGBge3J9DQphdmdfYWxsZWxlX25fcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcSwgc3RyZWFtID0gIkZpc2ggLSBEb3VnbGFzIElzbGFuZCIpDQpgYGANCg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCmFkYXB0aXZlX2ZyZXFfcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcSAlPiUgZHBseXI6OmZpbHRlcih5ZWFyICE9IDIwMjApLCBzdHJlYW0gPSAiRmlzaCAtIERvdWdsYXMgSXNsYW5kIikgICMgZGl0Y2ggMjAyMDsgbG93IG4NCmBgYA0KDQpJbmNvbnNpc3RlbnQgcGF0dGVybnMgYW1vbmcgeWVhcnMgZm9yIExHMzUgbG9jaSwgYnV0IExHMjkgbG9jaSBhcmUgcmVhc29uYWJseSBjb25zaXN0ZW50Lg0KDQojIyMjIFByb3NwZWN0DQoNClNob3cgYXZlcmFnZSBhbGxlbGUgc2FtcGxlIHNpemVzIHBlciBET1kgYnkgeWVhci4NCmBgYHtyfQ0KYXZnX2FsbGVsZV9uX3Bsb3QoZnJlcSA9IGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXEsIHN0cmVhbSA9ICJQcm9zcGVjdCIpDQpgYGANCg0KKipOT1RFKiogc2FtcGxlIHNpemVzIGFyZSBsYWNrbHVzdGVyIGZvciAyMDIwIGFuZCAyMDIxLCBhcyBhcmUgdGhlIHRhaWxzIG9mIHRoZSBydW4gaW4gMjAxOC4NCg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCmFkYXB0aXZlX2ZyZXFfcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcSwgc3RyZWFtID0gIlByb3NwZWN0IikNCmBgYA0KDQpJbmNvbnNpc3RlbnQgcGF0dGVybnMgYW1vbmcgeWVhcnMgZm9yIExHMjkgbG9jaSAoc2VlIGBMRzI5XzI1NDU1ODMzYCBmb3IgMjAxNCB2cy4gMjAxOSEpLCBidXQgTEczNSBsb2NpIGFyZSByZWFzb25hYmx5IGNvbnNpc3RlbnQuDQoNCiMjIyMgU2F3bWlsbA0KDQpTaG93IGF2ZXJhZ2UgYWxsZWxlIHNhbXBsZSBzaXplcyBwZXIgRE9ZIGJ5IHllYXIuDQpgYGB7cn0NCmF2Z19hbGxlbGVfbl9wbG90KGZyZXEgPSBjaHVtX2FkYXB0aXZlX2FsbGVsZV9mcmVxLCBzdHJlYW0gPSAiU2F3bWlsbCIpDQpgYGANCg0KKipOT1RFKiogc2FtcGxlIHNpemVzIHJlYXNvbmFibGUgZm9yIGFsbCBleGNlcHQgMjAyMC4NCg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCmFkYXB0aXZlX2ZyZXFfcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcSAlPiUgZHBseXI6OmZpbHRlcih5ZWFyICE9IDIwMjApLCBzdHJlYW0gPSAiU2F3bWlsbCIpICAjIGRpdGNoIDIwMjA7IGxvdyBuDQpgYGANCg0KTW9yZSBzaW1pbGFyIHRvIEZpc2ggQ3JlZWsgLSBEb3VnbGFzIElzbGFuZC4gV2VhaywgaW5jb25zaXN0ZW50IHJlbGF0aW9uc2hpcCBmb3IgTEczNSBsb2NpLCBidXQgTEcyOSBsb2NpIGhhdmUgYSBjb25zaXN0ZW50IHJlbGF0aW9uc2hpcCBhY3Jvc3MgeWVhcnMuDQoNCiMjIyBOYXR1cmFsLW9yaWdpbg0KDQpPbmx5IGluY2x1ZGVzIG5hdHVyYWwtb3JpZ2luIGZpc2guDQoNCiMjIyMgQWRtaXJhbHR5DQoNClNob3cgYXZlcmFnZSBhbGxlbGUgc2FtcGxlIHNpemVzIHBlciBET1kgYnkgeWVhci4NCmBgYHtyfQ0KYXZnX2FsbGVsZV9uX3Bsb3QoZnJlcSA9IGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXFfd2lsZCwgc3RyZWFtID0gIkFkbWlyYWx0eSIpDQpgYGANCg0KVmVyeSBmZXcgaGF0Y2hlcnkgc3RyYXlzIGludG8gQWRtaXJhbHR5LCBubyBiaWcgY2hhbmdlcyBpbiBzYW1wbGUgc2l6ZXMuDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTJ9DQphZGFwdGl2ZV9mcmVxX3Bsb3QoZnJlcSA9IGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXFfd2lsZCwgc3RyZWFtID0gIkFkbWlyYWx0eSIpDQpgYGANCg0KU2ltaWxhciBwYXR0ZXJucyBhcyBub3RlZCBhYm92ZS4NCg0KIyMjIyBGaXNoDQoNClNob3cgYXZlcmFnZSBhbGxlbGUgc2FtcGxlIHNpemVzIHBlciBET1kgYnkgeWVhci4NCmBgYHtyfQ0KYXZnX2FsbGVsZV9uX3Bsb3QoZnJlcSA9IGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXFfd2lsZCAlPiUgZHBseXI6OmZpbHRlcih5ZWFyICE9IDIwMjApLCBzdHJlYW0gPSAiRmlzaCAtIERvdWdsYXMgSXNsYW5kIikgIyBkaXRjaCAyMDIwOyBsb3cgbg0KYGBgDQoNCkxvdCBvZiBoYXRjaGVyeSBzdHJheXMgaW4gRmlzaCBDcmVlayAtIERvdWdsYXMgSXNsYW5kISBHb29kIHNhbXBsZSBzaXplcyB0aHJvdWdob3V0IHRoZSBydW4gZm9yIG1vc3QgeWVhcnMsIGV4Y2x1ZGluZyAyMDEzIChzcGFyc2Ugc2FtcGxpbmcpIGFuZCAyMDIxIChsb3cgbikuDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTJ9DQphZGFwdGl2ZV9mcmVxX3Bsb3QoZnJlcSA9IGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXFfd2lsZCAlPiUgZHBseXI6OmZpbHRlcih5ZWFyICE9IDIwMjApLCBzdHJlYW0gPSAiRmlzaCAtIERvdWdsYXMgSXNsYW5kIikgICMgZGl0Y2ggMjAyMDsgbG93IG4NCmBgYA0KDQpTb21lIHN1YnRsZSBjaGFuZ2VzIGluIHJlbGF0aW9uc2hpcHMgd2hlbiBleGNsdWRpbmcgaGF0Y2hlcnkgc3RyYXlzLCBidXQgZ2VuZXJhbCBwYXR0ZXJuIG9mIHN0cm9uZ2VyIHJlbGF0aW9uc2hpcCB3aXRoIExHMjkgbG9jaSBob2xkcy4NCg0KIyMjIyBQcm9zcGVjdA0KDQpTaG93IGF2ZXJhZ2UgYWxsZWxlIHNhbXBsZSBzaXplcyBwZXIgRE9ZIGJ5IHllYXIuDQpgYGB7cn0NCmF2Z19hbGxlbGVfbl9wbG90KGZyZXEgPSBjaHVtX2FkYXB0aXZlX2FsbGVsZV9mcmVxX3dpbGQsIHN0cmVhbSA9ICJQcm9zcGVjdCIpDQpgYGANCg0KKipOT1RFKiogc2FtcGxlIHNpemVzIHJlbWFpbiBsYWNrbHVzdGVyIGZvciAyMDIwIGFuZCAyMDIxLCBhcyBhcmUgdGhlIHRhaWxzIG9mIHRoZSBydW4gaW4gMjAxOC4NCg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCmFkYXB0aXZlX2ZyZXFfcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcV93aWxkLCBzdHJlYW0gPSAiUHJvc3BlY3QiKQ0KYGBgDQoNCk1pbmltYWwgY2hhbmdlcyB0byByZWxhdGlvbnNoaXBzIGFmdGVyIGV4Y2x1ZGluZyBoYXRjaGVyeSBzdHJheXMuDQoNCiMjIyMgU2F3bWlsbA0KDQpTaG93IGF2ZXJhZ2UgYWxsZWxlIHNhbXBsZSBzaXplcyBwZXIgRE9ZIGJ5IHllYXIuDQpgYGB7cn0NCmF2Z19hbGxlbGVfbl9wbG90KGZyZXEgPSBjaHVtX2FkYXB0aXZlX2FsbGVsZV9mcmVxX3dpbGQsIHN0cmVhbSA9ICJTYXdtaWxsIikNCmBgYA0KDQoqKk5PVEUqKiBzYW1wbGUgc2l6ZXMgcmVhc29uYWJsZSBmb3IgYWxsIGV4Y2VwdCAyMDIwLg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEyfQ0KYWRhcHRpdmVfZnJlcV9wbG90KGZyZXEgPSBjaHVtX2FkYXB0aXZlX2FsbGVsZV9mcmVxX3dpbGQgJT4lIGRwbHlyOjpmaWx0ZXIoeWVhciAhPSAyMDIwKSwgc3RyZWFtID0gIlNhd21pbGwiKSAgIyBkaXRjaCAyMDIwDQpgYGANCg0KVGhlIHBhdHRlcm4gb2Ygc3Ryb25nZXIgcmVsYXRpb25zaGlwcyB3aXRoIExHMjkgbG9jaSBob2xkcy4NCg0KIyMjIE5hdHVyYWwtb3JpZ2luICsgQWRqdXN0IGZvciBTcGF3bmluZyBTdGF0ZQ0KDQpPbmx5IGluY2x1ZGVzIG5hdHVyYWwtb3JpZ2luIGZpc2ggYW5kIGFkanVzdG1lbnRzIHRvIGNvbGxlY3Rpb24gZGF0ZSAoRE9ZKSBiYXNlZCBvbiBzcGF3bmluZyBzdGF0ZSAocmUtc2NhbGVkIHRvICpBbGl2ZSopLg0KDQojIyMjIEFkbWlyYWx0eQ0KDQpTaG93IGF2ZXJhZ2UgYWxsZWxlIHNhbXBsZSBzaXplcyBwZXIgRE9ZIGJ5IHllYXIuDQpgYGB7cn0NCmF2Z19hbGxlbGVfbl9wbG90KGZyZXEgPSBjaHVtX2FkYXB0aXZlX2FsbGVsZV9mcmVxX3dpbGRfYWRqX3NwYXduaW5nLCBzdHJlYW0gPSAiQWRtaXJhbHR5IikNCmBgYA0KDQpEaXN0cmlidXRpb24gY2hhbmdlcyBhIGJpdCBkdWUgdG8gYHNwYXduaW5nX3N0YXRlYCBhZGp1c3RtZW50IHRvIERPWS4NCg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCmFkYXB0aXZlX2ZyZXFfcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcV93aWxkX2Fkal9zcGF3bmluZywgc3RyZWFtID0gIkFkbWlyYWx0eSIpDQpgYGANCg0KQWRqdXN0aW5nIERPWSBmb3IgYHNwYXduaW5nX3N0YXRlYCBkb2Vzbid0IGFwcGVhciB0byBhbHRlciByZWxhdGlvbnNoaXBzIG11Y2ggZm9yIEFkbWlyYWx0eS4gTEcyOSBsb2NpIGhhdmUgYSBzdHJvbmdlciBhc3NvY2lhdGlvbiB3aXRoIHJ1biB0aW1pbmcgdGhhbiBMRzM1Lg0KDQojIyMjIEZpc2gNCg0KU2hvdyBhdmVyYWdlIGFsbGVsZSBzYW1wbGUgc2l6ZXMgcGVyIERPWSBieSB5ZWFyLg0KYGBge3J9DQphdmdfYWxsZWxlX25fcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcV93aWxkX2Fkal9zcGF3bmluZyAlPiUgZHBseXI6OmZpbHRlcih5ZWFyICE9IDIwMjApLCBzdHJlYW0gPSAiRmlzaCAtIERvdWdsYXMgSXNsYW5kIikgIyBkaXRjaCAyMDIwOyBsb3cgbg0KYGBgDQoNCkxvdCBvZiBoYXRjaGVyeSBzdHJheXMgaW4gRmlzaCBDcmVlayAtIERvdWdsYXMgSXNsYW5kISBHb29kIHNhbXBsZSBzaXplcyB0aHJvdWdob3V0IHRoZSBydW4gZm9yIG1vc3QgeWVhcnMsIGV4Y2x1ZGluZyAyMDEzIChzcGFyc2Ugc2FtcGxpbmcpIGFuZCAyMDIxIChsb3cgbikuDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTJ9DQphZGFwdGl2ZV9mcmVxX3Bsb3QoZnJlcSA9IGNodW1fYWRhcHRpdmVfYWxsZWxlX2ZyZXFfd2lsZF9hZGpfc3Bhd25pbmcgJT4lIGRwbHlyOjpmaWx0ZXIoeWVhciAhPSAyMDIwKSwgc3RyZWFtID0gIkZpc2ggLSBEb3VnbGFzIElzbGFuZCIpICAjIGRpdGNoIDIwMjANCmBgYA0KDQpBZGp1c3RpbmcgRE9ZIGZvciBgc3Bhd25pbmdfc3RhdGVgIGRlZmluaXRlbHkgaW50cm9kdWNlcyBzb21lIG1vcmUgbm9pc2UsIGJ1dCBMRzI5IGxvY2kgc3RpbGwgaGF2ZSBhIHN0cm9uZ2VyIGFzc29jaWF0aW9uIHdpdGggcnVuIHRpbWluZyB0aGFuIExHMzUuDQoNCiMjIyMgUHJvc3BlY3QNCg0KU2hvdyBhdmVyYWdlIGFsbGVsZSBzYW1wbGUgc2l6ZXMgcGVyIERPWSBieSB5ZWFyLg0KYGBge3J9DQphdmdfYWxsZWxlX25fcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcV93aWxkX2Fkal9zcGF3bmluZywgc3RyZWFtID0gIlByb3NwZWN0IikNCmBgYA0KDQoqKk5PVEUqKiBiaWdnZXIgc2hpZnQgaW4gdGhlIGRpc3RyaWJ1dGlvbiBvZiBET1kgZm9yIDIwMTMgYW5kIDIwMTkgdGhhbiBvdGhlciB5ZWFycy4NCg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCmFkYXB0aXZlX2ZyZXFfcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcV93aWxkX2Fkal9zcGF3bmluZywgc3RyZWFtID0gIlByb3NwZWN0IikNCmBgYA0KDQpBZGp1c3RpbmcgRE9ZIGZvciBgc3Bhd25pbmdfc3RhdGVgIGRlZmluaXRlbHkgaW50cm9kdWNlcyBzb21lIG1vcmUgbm9pc2UsIGJ1dCBjbGVhbnMgdXAgdGhlIHJlbGF0aW9uc2hpcHMgd2l0aCBMRzI5IGxvY2kgKGV4Y2VwdCBmb3IgYExHMjlfMjU0NTU4MzNgLCBkdW5ubyB3aGF0IGlzIGdvaW5nIG9uIHdpdGggdGhvc2UgYnJvb2QgeWVhciBlZmZlY3RzISkuDQoNCiMjIyMgU2F3bWlsbA0KDQpTaG93IGF2ZXJhZ2UgYWxsZWxlIHNhbXBsZSBzaXplcyBwZXIgRE9ZIGJ5IHllYXIuDQpgYGB7cn0NCmF2Z19hbGxlbGVfbl9wbG90KGZyZXEgPSBjaHVtX2FkYXB0aXZlX2FsbGVsZV9mcmVxX3dpbGRfYWRqX3NwYXduaW5nLCBzdHJlYW0gPSAiU2F3bWlsbCIpDQpgYGANCg0KKipOT1RFKiogc2FtcGxlIHNpemVzIGdldHRpbmcgZmFpcmx5IHNtYWxsIHBlciBET1ksIGJ1dCBnb29kIGRpc3RyaWJ1dGlvbi4NCg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCmFkYXB0aXZlX2ZyZXFfcGxvdChmcmVxID0gY2h1bV9hZGFwdGl2ZV9hbGxlbGVfZnJlcV93aWxkX2Fkal9zcGF3bmluZyAlPiUgZHBseXI6OmZpbHRlcih5ZWFyICE9IDIwMjApLCBzdHJlYW0gPSAiU2F3bWlsbCIpICAjIGRpdGNoIDIwMjANCmBgYA0KDQpBZGp1c3RpbmcgRE9ZIGZvciBgc3Bhd25pbmdfc3RhdGVgIGFwcGVhcnMgdG8gaGF2ZSByZXZlcnNlZCB0aGUgcmVsYXRpb25zaGlwcyBmb3IgMjAxOSBmb3IgYExHMjlfMjU0MDc1MmAgYW5kIGBMR18yNTQ4MzU5NWA/DQoNCiMjIEV4cG9ydCBHZW5vdHlwZXMgZm9yIE5PQUEgQUJMDQoNClNhdmUgYXMgYm90aCBhIGAuY3N2YCBhbmQgYC5yZHNgLg0KYGBge3J9DQpyZWFkcjo6d3JpdGVfY3N2KHggPSBjaHVtX2FkYXB0aXZlX2dlbm8sIGZpbGUgPSAib3V0cHV0L2NodW1fYWRhcHRpdmVfZ2Vub18yMDI1LTA3LTMwLmNzdiIpDQpHQ0xyOjpzYXZlX29iamVjdHMob2JqZWN0cyA9ICJjaHVtX2FkYXB0aXZlX2dlbm8iLCBwYXRoID0gIm91dHB1dCIsIHJkcyA9IFRSVUUpDQpgYGANCg0KIyBDb25jbHVzaW9ucw0KDQpOaW5lIGFkYXB0aXZlIGxvY2kgYXNzb2NpYXRlZCB3aXRoIHJ1biB0aW1pbmcgd2VyZSBpZGVudGlmaWVkIGJ5IE5PQUEgQUJMIGFuZCBpbmNsdWRlZCBpbiB0aGUgQURGJkcgQUhSUCBjaHVtIHNhbG1vbiBwYXJlbnRhZ2UgR1Qtc2VxIHBhbmVsLiB+MTlLIFNFQUsgY2h1bSBBSFJQIHBlZGlncmVlIHNhbXBsZXMgd2VyZSBnZW5vdHlwZWQgYXQgdGhlIEdDTC4gfjRLIHNhbXBsZXMgd2VyZSByZW1vdmVkIGZyb20gZnVydGhlciBhbmFseXNlcyBkdWUgdG8gcGVuZGluZyBRQyBjcm9zcyBjaGVja3MsIHBvb3IgcXVhbGl0eSBETkEsIGNvbnRhbWluYXRlZCBzYW1wbGVzLCBvciBkdXBsaWNhdGUgZ2Vub3R5cGVzLCBsZWF2aW5nIGEgdG90YWwgb2YgfjE1SyBzYW1wbGVzIGNvbGxlY3RlZCBmcm9tIDIwMTMtMjAyMy4gV2VpZ2h0ZWQgbGluZWFyIHJlZ3Jlc3Npb25zIHdlcmUgcGVyZm9ybWVkIHRvIGludmVzdGlnYXRlIHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIGFkYXB0aXZlIGxvY2kgYWxsZWxlIGZyZXF1ZW5jaWVzIGFuZCBydW4gdGltaW5nIChjb2xsZWN0aW9uIGRhdGUpIHVzaW5nIGNvZGUgYWRhcHRlZCBmcm9tIFBhdCBCYXJyeSAoTk9BQSBBQkwpLiBOb3RlIHRoYXQgY29sbGVjdGlvbiBkYXRlIGlzIGFuIGltcGVyZmVjdCBwcm94eSBvZiBydW4gdGltaW5nIGZvciB0aGVzZSBmaXNoIGdpdmVuIHRoYXQgc2FtcGxlcyB3ZXJlIGNvbGxlY3RlZCBmcm9tIGJvdGggbGl2aW5nIGZpc2ggYW5kIHBvc3Qtc3Bhd24gY2FyY2Fzc2VzISBBZGp1c3RpbmcgY29sbGVjdGlvbiBkYXRlIGZvciBmaXNoIGNvbmRpdGlvbiAoYHNwYXduaW5nX3N0YXRlYCkgYXBwZWFycyB0byBoYXZlIGltcHJvdmVkIHNvbWUgcmVsYXRpb25zaGlwcy4gVGhlIHN0cmVuZ3RoIG9mIGFzc29jaWF0aW9uIGFwcGVhcnMgdG8gdmFyeSBib3RoIGJ5IHN0cmVhbSwgeWVhciwgYW5kIG9yaWdpbiAoaGF0Y2hlcnkgdnMuIG5hdHVyYWwpLCB3aXRoIHNvbWUgYXBwYXJlbnQgYnJvb2QgeWVhciBlZmZlY3RzIChhc3N1bWluZyBtb3N0IGNodW0gYXJlIGFnZS00KS4gVGhlIHNvbGUgTEcyMyBTTlAgc2hvd3MgaW5jb25zaXN0ZW50IGFuZCBhdCB0aW1lcyBjb250cmFzdGluZyBwYXR0ZXJucyBhY3Jvc3MgeWVhcnMsIHdoZXJlYXMgU05QcyBvbiBMRzI5IChFU1JCKSBhbmQgTEczNSAoTFJSQzkpIHNob3cgbW9yZSBjb25zaXN0ZW5jeSBhY3Jvc3Mgc3RyZWFtcyBhbmQgeWVhcnMuIExHMjkgbG9jaSAoZXhjZXB0IGBMR18yNTQ1NTgzM2ApIHdlcmUgbW9yZSBjb25zaXN0ZW50bHkgYXNzb2NpYXRlZCB3aXRoIHJ1biB0aW1pbmcgdGhhbiBMRzM1IGxvY2kgaW4gYWxsIHN0cmVhbXMgYWZ0ZXIgYWRqdXN0aW5nIERPWSB0byBhY2NvdW50IGZvciBgc3Bhd25pbmdfc3RhdGVgLiANCg0KZW5k